use of com.hazelcast.config.cp.RaftAlgorithmConfig in project hazelcast by hazelcast.
the class PersistenceTest method when_leaderIsRestarted_then_itBecomesFollowerAndRestoresItsRaftStateWithSnapshot.
@Test
public void when_leaderIsRestarted_then_itBecomesFollowerAndRestoresItsRaftStateWithSnapshot() throws ExecutionException, InterruptedException {
final int committedEntryCountToSnapshot = 50;
RaftAlgorithmConfig config = new RaftAlgorithmConfig().setCommitIndexAdvanceCountToSnapshot(committedEntryCountToSnapshot);
group = new LocalRaftGroupBuilder(3, config).setAppendNopEntryOnLeaderElection(true).setRaftStateStoreFactory(RAFT_STATE_STORE_FACTORY).build();
group.start();
RaftNodeImpl leader = group.waitUntilLeaderElected();
for (int i = 0; i <= committedEntryCountToSnapshot; i++) {
leader.replicate(new ApplyRaftRunnable("val" + i)).get();
}
assertTrue(getSnapshotEntry(leader).index() > 0);
RaftEndpoint terminatedEndpoint = leader.getLocalMember();
InMemoryRaftStateStore stateStore = getRaftStateStore(leader);
RestoredRaftState terminatedState = stateStore.toRestoredRaftState();
group.terminateNode(terminatedEndpoint);
final RaftNodeImpl newLeader = group.waitUntilLeaderElected();
final RaftNodeImpl restartedNode = group.createNewRaftNode(terminatedState, stateStore);
assertEquals(new ArrayList<>(getCommittedGroupMembers(newLeader).members()), new ArrayList<>(getCommittedGroupMembers(restartedNode).members()));
assertEquals(new ArrayList<>(getLastGroupMembers(newLeader).members()), new ArrayList<>(getLastGroupMembers(restartedNode).members()));
assertTrueEventually(() -> {
assertEquals(newLeader.getLocalMember(), restartedNode.getLeader());
assertEquals(getTerm(newLeader), getTerm(restartedNode));
assertEquals(getCommitIndex(newLeader), getCommitIndex(restartedNode));
assertEquals(getLastApplied(newLeader), getLastApplied(restartedNode));
RaftDataService service = group.getService(restartedNode);
Object[] values = service.valuesArray();
assertThat(values, arrayWithSize(committedEntryCountToSnapshot + 1));
for (int i = 0; i <= committedEntryCountToSnapshot; i++) {
assertEquals("val" + i, values[i]);
}
});
}
use of com.hazelcast.config.cp.RaftAlgorithmConfig in project hazelcast by hazelcast.
the class SnapshotTest method when_followersIsFarBehind_then_itInstallsSnapshot.
@Test
public void when_followersIsFarBehind_then_itInstallsSnapshot() throws ExecutionException, InterruptedException {
final int entryCount = 50;
RaftAlgorithmConfig config = new RaftAlgorithmConfig().setCommitIndexAdvanceCountToSnapshot(entryCount);
group = newGroup(3, config);
group.start();
RaftNodeImpl leader = group.waitUntilLeaderElected();
leader.replicate(new ApplyRaftRunnable("val0")).get();
RaftNodeImpl[] followers = group.getNodesExcept(leader.getLocalMember());
RaftNodeImpl slowFollower = followers[1];
group.dropMessagesToMember(leader.getLocalMember(), slowFollower.getLocalMember(), AppendRequest.class);
group.dropMessagesToMember(leader.getLocalMember(), slowFollower.getLocalMember(), InstallSnapshot.class);
for (int i = 1; i < entryCount; i++) {
leader.replicate(new ApplyRaftRunnable("val" + i)).get();
}
assertTrueEventually(() -> assertEquals(entryCount, getSnapshotEntry(leader).index()));
leader.replicate(new ApplyRaftRunnable("valFinal")).get();
group.allowMessagesToMember(leader.getLocalMember(), slowFollower.getLocalMember(), InstallSnapshot.class);
assertTrueEventually(() -> assertEquals(entryCount, getCommitIndex(slowFollower)));
group.resetAllRulesFrom(leader.getLocalMember());
assertTrueEventually(() -> {
for (RaftNodeImpl raftNode : group.getNodes()) {
assertEquals(entryCount + 1, getCommitIndex(raftNode));
RaftDataService service = group.getService(raftNode);
assertEquals(entryCount + 1, service.size());
for (int i = 0; i < entryCount; i++) {
assertEquals(("val" + i), service.get(i + 1));
}
assertEquals("valFinal", service.get(51));
}
});
}
use of com.hazelcast.config.cp.RaftAlgorithmConfig in project hazelcast by hazelcast.
the class SnapshotTest method when_followerMissesTheLastEntryThatGoesIntoTheSnapshot_then_itCatchesUpWithoutInstallingSnapshot.
@Test
public void when_followerMissesTheLastEntryThatGoesIntoTheSnapshot_then_itCatchesUpWithoutInstallingSnapshot() throws ExecutionException, InterruptedException {
final int entryCount = 50;
RaftAlgorithmConfig config = new RaftAlgorithmConfig().setCommitIndexAdvanceCountToSnapshot(entryCount);
group = newGroup(3, config);
group.start();
RaftNodeImpl leader = group.waitUntilLeaderElected();
RaftNodeImpl[] followers = group.getNodesExcept(leader.getLocalMember());
RaftNodeImpl slowFollower = followers[1];
for (int i = 0; i < entryCount - 1; i++) {
leader.replicate(new ApplyRaftRunnable("val" + i)).get();
}
assertTrueEventually(() -> {
for (RaftNodeImpl follower : group.getNodesExcept(leader.getLocalMember())) {
assertEquals(entryCount - 1, getMatchIndex(leader, follower.getLocalMember()));
}
});
group.dropMessagesToMember(leader.getLocalMember(), slowFollower.getLocalMember(), AppendRequest.class);
group.dropMessagesToMember(leader.getLocalMember(), slowFollower.getLocalMember(), InstallSnapshot.class);
leader.replicate(new ApplyRaftRunnable("val" + (entryCount - 1))).get();
assertTrueEventually(() -> assertEquals(entryCount, getSnapshotEntry(leader).index()));
leader.replicate(new ApplyRaftRunnable("valFinal")).get();
group.allowMessagesToMember(leader.getLocalMember(), slowFollower.getLocalMember(), AppendRequest.class);
assertTrueEventually(() -> {
for (RaftNodeImpl raftNode : group.getNodes()) {
assertEquals(entryCount + 1, getCommitIndex(raftNode));
RaftDataService service = group.getService(raftNode);
assertEquals(entryCount + 1, service.size());
for (int i = 0; i < entryCount; i++) {
assertEquals(("val" + i), service.get(i + 1));
}
assertEquals("valFinal", service.get(51));
}
});
}
use of com.hazelcast.config.cp.RaftAlgorithmConfig in project hazelcast by hazelcast.
the class SnapshotTest method testMembershipChangeBlocksSnapshotBug.
@Test
public void testMembershipChangeBlocksSnapshotBug() throws ExecutionException, InterruptedException {
// The comments below show how the code behaves before the mentioned bug is fixed.
int commitIndexAdvanceCount = 50;
int uncommittedEntryCount = 10;
RaftAlgorithmConfig config = new RaftAlgorithmConfig().setCommitIndexAdvanceCountToSnapshot(commitIndexAdvanceCount).setUncommittedEntryCountToRejectNewAppends(uncommittedEntryCount);
group = newGroup(3, config);
group.start();
RaftNodeImpl leader = group.waitUntilLeaderElected();
RaftNodeImpl[] followers = group.getNodesExcept(leader.getLocalMember());
group.dropMessagesToMember(leader.getLocalMember(), followers[0].getLocalMember(), AppendRequest.class);
while (getSnapshotEntry(leader).index() == 0) {
leader.replicate(new ApplyRaftRunnable("into_snapshot")).get();
}
// now, the leader has taken a snapshot.
// It also keeps some already committed entries in the log because followers[0] hasn't appended them.
// LOG: [ <46 - 49>, <50>], SNAPSHOT INDEX: 50, COMMIT INDEX: 50
long leaderCommitIndex = getCommitIndex(leader);
do {
leader.replicate(new ApplyRaftRunnable("committed_after_snapshot")).get();
} while (getCommitIndex(leader) < leaderCommitIndex + commitIndexAdvanceCount - 1);
// committing new entries.
// one more entry is needed to take the next snapshot.
// LOG: [ <46 - 49>, <50>, <51 - 99 (committed)> ], SNAPSHOT INDEX: 50, COMMIT INDEX: 99
group.dropMessagesToMember(leader.getLocalMember(), followers[1].getLocalMember(), AppendRequest.class);
for (int i = 0; i < uncommittedEntryCount - 1; i++) {
leader.replicate(new ApplyRaftRunnable("uncommitted_after_snapshot"));
}
// appended some more entries which will not be committed because the leader has no majority.
// the last uncommitted index is reserved for membership changed.
// LOG: [ <46 - 49>, <50>, <51 - 99 (committed)>, <100 - 108 (uncommitted)> ], SNAPSHOT INDEX: 50, COMMIT INDEX: 99
// There are only 2 empty indices in the log.
RaftNodeImpl newRaftNode = group.createNewRaftNode();
Function<Object, Object> alterFunc = o -> {
if (o instanceof AppendRequest) {
AppendRequest request = (AppendRequest) o;
LogEntry[] entries = request.entries();
if (entries.length > 0) {
if (entries[entries.length - 1].operation() instanceof UpdateRaftGroupMembersCmd) {
entries = Arrays.copyOf(entries, entries.length - 1);
return new AppendRequest(request.leader(), request.term(), request.prevLogTerm(), request.prevLogIndex(), request.leaderCommitIndex(), entries, request.queryRound());
} else if (entries[0].operation() instanceof UpdateRaftGroupMembersCmd) {
entries = new LogEntry[0];
return new AppendRequest(request.leader(), request.term(), request.prevLogTerm(), request.prevLogIndex(), request.leaderCommitIndex(), entries, request.queryRound());
}
}
}
return null;
};
group.alterMessagesToMember(leader.getLocalMember(), followers[1].getLocalMember(), alterFunc);
group.alterMessagesToMember(leader.getLocalMember(), newRaftNode.getLocalMember(), alterFunc);
long lastLogIndex1 = getLastLogOrSnapshotEntry(leader).index();
leader.replicateMembershipChange(newRaftNode.getLocalMember(), MembershipChangeMode.ADD);
// When the membership change entry is appended, the leader's Log will be as following:
// LOG: [ <46 - 49>, <50>, <51 - 99 (committed)>, <100 - 108 (uncommitted)>, <109 (membership change)> ], SNAPSHOT INDEX: 50, COMMIT INDEX: 99
assertTrueEventually(() -> assertTrue(getLastLogOrSnapshotEntry(leader).index() > lastLogIndex1));
group.allowMessagesToMember(leader.getLocalMember(), followers[1].getLocalMember(), AppendRequest.class);
// Then, only the entries before the membership change will be committed because we alter the append request. The log will be:
// LOG: [ <46 - 49>, <50>, <51 - 108 (committed)>, <109 (membership change)> ], SNAPSHOT INDEX: 50, COMMIT INDEX: 108
// There is only 1 empty index in the log.
assertTrueEventually(() -> {
assertEquals(lastLogIndex1, getCommitIndex(leader));
assertEquals(lastLogIndex1, getCommitIndex(followers[1]));
});
// assertTrueEventually(() -> {
// assertEquals(lastLogIndex1 + 1, getCommitIndex(leader));
// assertEquals(lastLogIndex1 + 1, getCommitIndex(followers[1]));
// });
long lastLogIndex2 = getLastLogOrSnapshotEntry(leader).index();
leader.replicate(new ApplyRaftRunnable("after_membership_change_append"));
assertTrueEventually(() -> assertTrue(getLastLogOrSnapshotEntry(leader).index() > lastLogIndex2));
// Now the log is full. There is no empty space left.
// LOG: [ <46 - 49>, <50>, <51 - 108 (committed)>, <109 (membership change)>, <110 (uncommitted)> ], SNAPSHOT INDEX: 50, COMMIT INDEX: 108
long lastLogIndex3 = getLastLogOrSnapshotEntry(leader).index();
Future f = leader.replicate(new ApplyRaftRunnable("after_membership_change_append"));
assertTrueEventually(() -> assertTrue(getLastLogOrSnapshotEntry(leader).index() > lastLogIndex3));
assertFalse(f.isDone());
}
use of com.hazelcast.config.cp.RaftAlgorithmConfig in project hazelcast by hazelcast.
the class LinearizableQueryTest method when_multipleQueryLimitIsReachedBeforeHeartbeatAcks_then_noNewQueryIsAccepted.
@Test(timeout = 300_000)
public void when_multipleQueryLimitIsReachedBeforeHeartbeatAcks_then_noNewQueryIsAccepted() throws Exception {
RaftAlgorithmConfig config = new RaftAlgorithmConfig().setUncommittedEntryCountToRejectNewAppends(1);
group = new LocalRaftGroupBuilder(5, config).setAppendNopEntryOnLeaderElection(true).build();
group.start();
RaftNodeImpl leader = group.waitUntilLeaderElected();
assertTrueEventually(() -> assertThat(getCommitIndex(leader), greaterThanOrEqualTo(1L)));
RaftNodeImpl[] followers = group.getNodesExcept(leader.getLocalMember());
group.dropMessagesToMember(leader.getLocalMember(), followers[0].getLocalMember(), AppendRequest.class);
group.dropMessagesToMember(leader.getLocalMember(), followers[1].getLocalMember(), AppendRequest.class);
group.dropMessagesToMember(leader.getLocalMember(), followers[2].getLocalMember(), AppendRequest.class);
InternalCompletableFuture queryFuture1 = leader.query(new QueryRaftRunnable(), LINEARIZABLE);
InternalCompletableFuture queryFuture2 = leader.query(new QueryRaftRunnable(), LINEARIZABLE);
try {
queryFuture2.joinInternal();
fail();
} catch (CannotReplicateException ignored) {
}
group.resetAllRulesFrom(leader.getLocalMember());
queryFuture1.get();
}
Aggregations