use of com.hazelcast.cp.internal.raft.impl.log.LogEntry in project hazelcast by hazelcast.
the class AppendRequestHandlerTask method innerRun.
@Override
@SuppressWarnings({ "checkstyle:npathcomplexity", "checkstyle:cyclomaticcomplexity", "checkstyle:methodlength", "checkstyle:nestedifdepth" })
protected // Justification: It is easier to follow the AppendEntriesRPC logic in a single method
void innerRun() {
if (logger.isFineEnabled()) {
logger.fine("Received " + req);
}
RaftState state = raftNode.state();
// Reply false if term < currentTerm (§5.1)
if (req.term() < state.term()) {
if (logger.isFineEnabled()) {
logger.warning("Stale " + req + " received in current term: " + state.term());
}
raftNode.send(createFailureResponse(state.term()), req.leader());
return;
}
// Transform into follower if a newer term is seen or another node wins the election of the current term
if (req.term() > state.term() || state.role() != FOLLOWER) {
// If RPC request or response contains term T > currentTerm: set currentTerm = T, convert to follower (§5.1)
logger.info("Demoting to FOLLOWER from current role: " + state.role() + ", term: " + state.term() + " to new term: " + req.term() + " and leader: " + req.leader());
raftNode.toFollower(req.term());
}
if (!req.leader().equals(state.leader())) {
logger.info("Setting leader: " + req.leader());
raftNode.leader(req.leader());
}
RaftLog raftLog = state.log();
// Verify the last log entry
if (req.prevLogIndex() > 0) {
long lastLogIndex = raftLog.lastLogOrSnapshotIndex();
int lastLogTerm = raftLog.lastLogOrSnapshotTerm();
int prevLogTerm;
if (req.prevLogIndex() == lastLogIndex) {
prevLogTerm = lastLogTerm;
} else {
// Reply false if log does not contain an entry at prevLogIndex whose term matches prevLogTerm (§5.3)
LogEntry prevLog = raftLog.getLogEntry(req.prevLogIndex());
if (prevLog == null) {
if (logger.isFineEnabled()) {
logger.warning("Failed to get previous log index for " + req + ", last log index: " + lastLogIndex);
}
raftNode.send(createFailureResponse(req.term()), req.leader());
return;
}
prevLogTerm = prevLog.term();
}
if (req.prevLogTerm() != prevLogTerm) {
if (logger.isFineEnabled()) {
logger.warning("Previous log term of " + req + " is different than ours: " + prevLogTerm);
}
raftNode.send(createFailureResponse(req.term()), req.leader());
return;
}
}
int truncatedAppendRequestEntryCount = 0;
LogEntry[] newEntries = null;
// Process any new entries
if (req.entryCount() > 0) {
// Delete any conflicting entries, skip any duplicates
long lastLogIndex = raftLog.lastLogOrSnapshotIndex();
for (int i = 0; i < req.entryCount(); i++) {
LogEntry reqEntry = req.entries()[i];
if (reqEntry.index() > lastLogIndex) {
newEntries = Arrays.copyOfRange(req.entries(), i, req.entryCount());
break;
}
LogEntry localEntry = raftLog.getLogEntry(reqEntry.index());
assert localEntry != null : "Entry not found on log index: " + reqEntry.index() + " for " + req;
// delete the existing entry and all that follow it (§5.3)
if (reqEntry.term() != localEntry.term()) {
List<LogEntry> truncatedEntries = raftLog.deleteEntriesFrom(reqEntry.index());
if (logger.isFineEnabled()) {
logger.warning("Truncated " + truncatedEntries.size() + " entries from entry index: " + reqEntry.index() + " => " + truncatedEntries);
} else {
logger.warning("Truncated " + truncatedEntries.size() + " entries from entry index: " + reqEntry.index());
}
raftNode.invalidateFuturesFrom(reqEntry.index());
revertPreAppliedRaftGroupCmd(truncatedEntries);
newEntries = Arrays.copyOfRange(req.entries(), i, req.entryCount());
raftLog.flush();
break;
}
}
if (newEntries != null && newEntries.length > 0) {
if (raftLog.availableCapacity() < newEntries.length) {
if (logger.isFineEnabled()) {
logger.warning("Truncating " + newEntries.length + " entries to " + raftLog.availableCapacity() + " to fit into the available capacity of the Raft log");
}
truncatedAppendRequestEntryCount = newEntries.length - raftLog.availableCapacity();
newEntries = Arrays.copyOf(newEntries, raftLog.availableCapacity());
}
// Append any new entries not already in the log
if (logger.isFineEnabled()) {
logger.fine("Appending " + newEntries.length + " entries: " + Arrays.toString(newEntries));
}
raftLog.appendEntries(newEntries);
raftLog.flush();
}
}
// I cannot use raftLog.lastLogOrSnapshotIndex() for lastLogIndex because my log may contain
// some uncommitted entries from the previous leader and those entries will be truncated soon
// I can only send a response based on how many entries I have appended from this append request
long lastLogIndex = req.prevLogIndex() + req.entryCount() - truncatedAppendRequestEntryCount;
long oldCommitIndex = state.commitIndex();
// Update the commit index
if (req.leaderCommitIndex() > oldCommitIndex) {
// If leaderCommit > commitIndex, set commitIndex = min(leaderCommit, index of last new entry)
long newCommitIndex = min(req.leaderCommitIndex(), lastLogIndex);
if (logger.isFineEnabled()) {
logger.fine("Setting commit index: " + newCommitIndex);
}
state.commitIndex(newCommitIndex);
}
raftNode.updateLastAppendEntriesTimestamp();
try {
AppendSuccessResponse resp = new AppendSuccessResponse(localMember(), state.term(), lastLogIndex, req.queryRound());
raftNode.send(resp, req.leader());
} finally {
if (state.commitIndex() > oldCommitIndex) {
raftNode.applyLogEntries();
}
if (newEntries != null) {
preApplyRaftGroupCmd(newEntries, state.commitIndex());
}
}
}
use of com.hazelcast.cp.internal.raft.impl.log.LogEntry in project hazelcast by hazelcast.
the class TriggerLeaderElectionHandlerTask method innerRun.
@Override
protected void innerRun() {
if (logger.isFineEnabled()) {
logger.fine("Received " + req);
}
RaftState state = raftNode.state();
// with the leader's log.
if (!(req.term() == state.term() && req.leader().equals(state.leader()))) {
if (logger.isFineEnabled()) {
logger.fine("Ignoring " + req + " since term: " + state.term() + " and leader: " + state.leader());
}
return;
}
// Verify the last log entry
LogEntry entry = state.log().lastLogOrSnapshotEntry();
if (!(entry.index() == req.lastLogIndex() && entry.term() == req.lastLogTerm())) {
if (logger.isFineEnabled()) {
logger.fine("Could not accept leadership transfer because local Raft log is not same with the current leader. " + "Last log entry: " + entry + ", request: " + req);
}
return;
}
// I will send a disruptive VoteRequest to bypass leader stickiness
logger.info("Starting a new leader election since the current leader: " + req.leader() + " in term: " + req.term() + " asked for a leadership transfer!");
state.leader(null);
new LeaderElectionTask(raftNode, true).run();
}
use of com.hazelcast.cp.internal.raft.impl.log.LogEntry in project hazelcast by hazelcast.
the class FencedLockAdvancedTest method testNewRaftGroupMemberSchedulesTimeoutsWithSnapshot.
@Test
public void testNewRaftGroupMemberSchedulesTimeoutsWithSnapshot() throws ExecutionException, InterruptedException {
long fence = this.lock.lockAndGetFence();
assertTrue(fence > 0);
spawn(() -> {
lock.tryLock(10, MINUTES);
});
CPGroupId groupId = this.lock.getGroupId();
assertTrueEventually(() -> {
HazelcastInstance leader = getLeaderInstance(instances, groupId);
LockService service = getNodeEngineImpl(leader).getService(LockService.SERVICE_NAME);
ResourceRegistry registry = service.getRegistryOrNull(groupId);
assertNotNull(registry);
assertFalse(registry.getWaitTimeouts().isEmpty());
});
spawn(() -> {
for (int i = 0; i < LOG_ENTRY_COUNT_TO_SNAPSHOT; i++) {
lock.isLocked();
}
});
assertTrueEventually(() -> {
for (HazelcastInstance instance : instances) {
RaftNodeImpl raftNode = getRaftNode(instance, groupId);
assertNotNull(raftNode);
LogEntry snapshotEntry = getSnapshotEntry(raftNode);
assertTrue(snapshotEntry.index() > 0);
List<RestoreSnapshotOp> ops = (List<RestoreSnapshotOp>) snapshotEntry.operation();
for (RestoreSnapshotOp op : ops) {
if (op.getServiceName().equals(LockService.SERVICE_NAME)) {
ResourceRegistry registry = (ResourceRegistry) op.getSnapshot();
assertFalse(registry.getWaitTimeouts().isEmpty());
return;
}
}
fail();
}
});
HazelcastInstance instanceToShutdown = (instances[0] == proxyInstance) ? instances[1] : instances[0];
instanceToShutdown.shutdown();
HazelcastInstance newInstance = factory.newHazelcastInstance(createConfig(groupSize, groupSize));
newInstance.getCPSubsystem().getCPSubsystemManagementService().promoteToCPMember().toCompletableFuture().get();
assertTrueEventually(() -> {
RaftNodeImpl raftNode = getRaftNode(newInstance, groupId);
assertNotNull(raftNode);
assertTrue(getSnapshotEntry(raftNode).index() > 0);
LockService service = getNodeEngineImpl(newInstance).getService(LockService.SERVICE_NAME);
LockRegistry registry = service.getRegistryOrNull(groupId);
assertNotNull(registry);
assertFalse(registry.getWaitTimeouts().isEmpty());
LockOwnershipState ownership = registry.getLockOwnershipState(objectName);
assertTrue(ownership.isLocked());
assertTrue(ownership.getLockCount() > 0);
assertEquals(fence, ownership.getFence());
});
}
use of com.hazelcast.cp.internal.raft.impl.log.LogEntry in project hazelcast by hazelcast.
the class CountDownLatchAdvancedTest method testNewRaftGroupMemberSchedulesTimeoutsWithSnapshot.
@Test
public void testNewRaftGroupMemberSchedulesTimeoutsWithSnapshot() throws ExecutionException, InterruptedException {
latch.trySetCount(1);
spawn(() -> {
try {
latch.await(10, MINUTES);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
CPGroupId groupId = getGroupId(latch);
assertTrueEventually(() -> {
HazelcastInstance leader = getLeaderInstance(instances, groupId);
CountDownLatchService service = getNodeEngineImpl(leader).getService(CountDownLatchService.SERVICE_NAME);
ResourceRegistry registry = service.getRegistryOrNull(groupId);
assertFalse(registry.getWaitTimeouts().isEmpty());
});
for (int i = 0; i < LOG_ENTRY_COUNT_TO_SNAPSHOT; i++) {
latch.trySetCount(1);
}
assertTrueEventually(() -> {
for (HazelcastInstance instance : instances) {
RaftNodeImpl raftNode = getRaftNode(instance, groupId);
assertNotNull(raftNode);
LogEntry snapshotEntry = getSnapshotEntry(raftNode);
assertTrue(snapshotEntry.index() > 0);
List<RestoreSnapshotOp> ops = (List<RestoreSnapshotOp>) snapshotEntry.operation();
for (RestoreSnapshotOp op : ops) {
if (op.getServiceName().equals(CountDownLatchService.SERVICE_NAME)) {
ResourceRegistry registry = (ResourceRegistry) op.getSnapshot();
assertFalse(registry.getWaitTimeouts().isEmpty());
return;
}
}
fail();
}
});
instances[1].shutdown();
HazelcastInstance newInstance = factory.newHazelcastInstance(createConfig(groupSize, groupSize));
newInstance.getCPSubsystem().getCPSubsystemManagementService().promoteToCPMember().toCompletableFuture().get();
assertTrueEventually(() -> {
CountDownLatchService service = getNodeEngineImpl(newInstance).getService(CountDownLatchService.SERVICE_NAME);
CountDownLatchRegistry registry = service.getRegistryOrNull(groupId);
assertNotNull(registry);
assertFalse(registry.getWaitTimeouts().isEmpty());
Assert.assertEquals(1, registry.getRemainingCount(objectName));
});
}
use of com.hazelcast.cp.internal.raft.impl.log.LogEntry in project hazelcast by hazelcast.
the class PersistenceTest method when_leaderAppendEntriesInMinoritySplit_then_itTruncatesEntriesOnStore.
@Test
public void when_leaderAppendEntriesInMinoritySplit_then_itTruncatesEntriesOnStore() throws ExecutionException, InterruptedException {
group = new LocalRaftGroupBuilder(3).setRaftStateStoreFactory(RAFT_STATE_STORE_FACTORY).build();
group.start();
final RaftNodeImpl leader = group.waitUntilLeaderElected();
leader.replicate(new ApplyRaftRunnable("val1")).get();
assertTrueEventually(() -> {
for (RaftNodeImpl raftNode : group.getNodes()) {
assertEquals(1, getCommitIndex(raftNode));
}
});
final RaftNodeImpl[] followers = group.getNodesExcept(leader.getLocalMember());
group.split(leader.getLocalMember());
assertTrueEventually(() -> {
for (RaftNodeImpl raftNode : followers) {
RaftEndpoint leaderEndpoint = getLeaderMember(raftNode);
assertNotNull(leaderEndpoint);
assertNotEquals(leader.getLocalMember(), leaderEndpoint);
}
});
for (int i = 0; i < 10; i++) {
leader.replicate(new ApplyRaftRunnable("isolated" + i));
}
assertTrueEventually(() -> {
RestoredRaftState restoredState = getRestoredState(leader);
LogEntry[] entries = restoredState.entries();
assertEquals(11, entries.length);
assertEquals("val1", ((ApplyRaftRunnable) entries[0].operation()).getVal());
for (int i = 1; i < 11; i++) {
assertEquals("isolated" + (i - 1), ((ApplyRaftRunnable) entries[i].operation()).getVal());
}
});
RaftNodeImpl newLeader = group.getNode(getLeaderMember(followers[0]));
for (int i = 0; i < 10; i++) {
newLeader.replicate(new ApplyRaftRunnable("valNew" + i)).get();
}
assertTrueEventually(() -> {
for (RaftNodeImpl raftNode : followers) {
assertEquals(11, getCommitIndex(raftNode));
}
});
group.merge();
RaftNodeImpl finalLeader = group.waitUntilLeaderElected();
assertNotEquals(leader.getLocalMember(), finalLeader.getLocalMember());
assertTrueEventually(() -> {
RestoredRaftState state = getRestoredState(leader);
LogEntry[] entries = state.entries();
assertEquals(11, entries.length);
assertEquals("val1", ((ApplyRaftRunnable) entries[0].operation()).getVal());
for (int i = 1; i < 11; i++) {
assertEquals("valNew" + (i - 1), ((ApplyRaftRunnable) entries[i].operation()).getVal());
}
});
}
Aggregations