use of org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshotReply in project controller by opendaylight.
the class ReplicationAndSnapshotsIntegrationTest method testSecondSnapshot.
/**
* Send one more payload to trigger another snapshot. In this scenario, we delay the snapshot until
* consensus occurs and the leader applies the state.
*/
private void testSecondSnapshot() throws Exception {
testLog.info("testSecondSnapshot starting");
expSnapshotState.add(payload3);
expSnapshotState.add(payload4);
expSnapshotState.add(payload5);
expSnapshotState.add(payload6);
// Delay the CaptureSnapshot message to the leader actor.
leaderActor.underlyingActor().startDropMessages(CaptureSnapshotReply.class);
// Send the payload.
payload7 = sendPayloadData(leaderActor, "seven");
// Capture the CaptureSnapshotReply message so we can send it later.
final CaptureSnapshotReply captureSnapshotReply = MessageCollectorActor.expectFirstMatching(leaderCollectorActor, CaptureSnapshotReply.class);
// Wait for the state to be applied in the leader.
ApplyState applyState = MessageCollectorActor.expectFirstMatching(leaderCollectorActor, ApplyState.class);
verifyApplyState(applyState, leaderCollectorActor, payload7.toString(), currentTerm, 7, payload7);
// At this point the leader has applied the new state but the cached snapshot index should not be
// advanced by a "fake" snapshot because we're in the middle of a snapshot. We'll wait for at least
// one more heartbeat AppendEntriesReply to ensure this does not occur.
MessageCollectorActor.clearMessages(leaderCollectorActor);
MessageCollectorActor.expectFirstMatching(leaderCollectorActor, AppendEntriesReply.class);
assertEquals("Leader snapshot term", currentTerm, leaderContext.getReplicatedLog().getSnapshotTerm());
assertEquals("Leader snapshot index", 5, leaderContext.getReplicatedLog().getSnapshotIndex());
assertEquals("Leader journal log size", 2, leaderContext.getReplicatedLog().size());
assertEquals("Leader journal last index", 7, leaderContext.getReplicatedLog().lastIndex());
assertEquals("Leader commit index", 7, leaderContext.getCommitIndex());
assertEquals("Leader last applied", 7, leaderContext.getLastApplied());
assertEquals("Leader replicatedToAllIndex", 5, leader.getReplicatedToAllIndex());
// Now deliver the CaptureSnapshotReply.
leaderActor.underlyingActor().stopDropMessages(CaptureSnapshotReply.class);
leaderActor.tell(captureSnapshotReply, leaderActor);
// Wait for snapshot complete.
MessageCollectorActor.expectFirstMatching(leaderCollectorActor, SaveSnapshotSuccess.class);
// Wait for another heartbeat AppendEntriesReply. This should cause a "fake" snapshot to advance the
// snapshot index and trimmed the log since we're no longer in a snapshot.
MessageCollectorActor.clearMessages(leaderCollectorActor);
MessageCollectorActor.expectFirstMatching(leaderCollectorActor, AppendEntriesReply.class);
assertEquals("Leader snapshot term", currentTerm, leaderContext.getReplicatedLog().getSnapshotTerm());
assertEquals("Leader snapshot index", 6, leaderContext.getReplicatedLog().getSnapshotIndex());
assertEquals("Leader journal log size", 1, leaderContext.getReplicatedLog().size());
assertEquals("Leader journal last index", 7, leaderContext.getReplicatedLog().lastIndex());
assertEquals("Leader commit index", 7, leaderContext.getCommitIndex());
// Verify the persisted snapshot. This should reflect the snapshot index as the last applied
// log entry (7) and shouldn't contain any unapplied entries as we capture persisted the snapshot data
// when the snapshot is created (ie when the CaptureSnapshot is processed).
List<Snapshot> persistedSnapshots = InMemorySnapshotStore.getSnapshots(leaderId, Snapshot.class);
assertEquals("Persisted snapshots size", 1, persistedSnapshots.size());
verifySnapshot("Persisted", persistedSnapshots.get(0), currentTerm, 6, currentTerm, 7);
List<ReplicatedLogEntry> unAppliedEntry = persistedSnapshots.get(0).getUnAppliedEntries();
assertEquals("Persisted Snapshot getUnAppliedEntries size", 1, unAppliedEntry.size());
verifyReplicatedLogEntry(unAppliedEntry.get(0), currentTerm, 7, payload7);
// The leader's persisted journal log should be cleared since we did a snapshot.
List<SimpleReplicatedLogEntry> persistedLeaderJournal = InMemoryJournal.get(leaderId, SimpleReplicatedLogEntry.class);
assertEquals("Persisted journal log size", 0, persistedLeaderJournal.size());
// Verify the followers apply all 4 new log entries.
List<ApplyState> applyStates = MessageCollectorActor.expectMatching(follower1CollectorActor, ApplyState.class, 4);
verifyApplyState(applyStates.get(0), null, null, currentTerm, 4, payload4);
verifyApplyState(applyStates.get(1), null, null, currentTerm, 5, payload5);
verifyApplyState(applyStates.get(2), null, null, currentTerm, 6, payload6);
verifyApplyState(applyStates.get(3), null, null, currentTerm, 7, payload7);
applyStates = MessageCollectorActor.expectMatching(follower2CollectorActor, ApplyState.class, 4);
verifyApplyState(applyStates.get(0), null, null, currentTerm, 4, payload4);
verifyApplyState(applyStates.get(1), null, null, currentTerm, 5, payload5);
verifyApplyState(applyStates.get(2), null, null, currentTerm, 6, payload6);
verifyApplyState(applyStates.get(3), null, null, currentTerm, 7, payload7);
// Verify the follower's snapshot index has also advanced. (after another AppendEntries heartbeat
// to be safe).
MessageCollectorActor.clearMessages(follower1CollectorActor);
MessageCollectorActor.expectFirstMatching(follower1CollectorActor, AppendEntries.class);
follower1Context = follower1Actor.underlyingActor().getRaftActorContext();
assertEquals("Follower 1 snapshot term", currentTerm, follower1Context.getReplicatedLog().getSnapshotTerm());
assertEquals("Follower 1 snapshot index", 6, follower1Context.getReplicatedLog().getSnapshotIndex());
assertEquals("Follower 1 journal log size", 1, follower1Context.getReplicatedLog().size());
assertEquals("Follower 1 journal last index", 7, follower1Context.getReplicatedLog().lastIndex());
assertEquals("Follower 1 commit index", 7, follower1Context.getCommitIndex());
MessageCollectorActor.clearMessages(follower2CollectorActor);
MessageCollectorActor.expectFirstMatching(follower2CollectorActor, AppendEntries.class);
follower2Context = follower2Actor.underlyingActor().getRaftActorContext();
assertEquals("Follower 2 snapshot term", currentTerm, follower2Context.getReplicatedLog().getSnapshotTerm());
assertEquals("Follower 2 snapshot index", 6, follower2Context.getReplicatedLog().getSnapshotIndex());
assertEquals("Follower 2 journal log size", 1, follower2Context.getReplicatedLog().size());
assertEquals("Follower 2 journal last index", 7, follower2Context.getReplicatedLog().lastIndex());
assertEquals("Follower 2 commit index", 7, follower2Context.getCommitIndex());
expSnapshotState.add(payload7);
testLog.info("testSecondSnapshot ending");
}
use of org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshotReply in project controller by opendaylight.
the class MigratedMessagesTest method testNoSnapshotAfterStartupWithNoMigratedMessages.
@Test
public void testNoSnapshotAfterStartupWithNoMigratedMessages() {
TEST_LOG.info("testNoSnapshotAfterStartupWithNoMigratedMessages starting");
String id = factory.generateActorId("test-actor-");
InMemoryJournal.addEntry(id, 1, new UpdateElectionTerm(1, id));
InMemoryJournal.addEntry(id, 2, new SimpleReplicatedLogEntry(0, 1, new MockRaftActorContext.MockPayload("A")));
InMemoryJournal.addEntry(id, 3, new ApplyJournalEntries(0));
DefaultConfigParamsImpl config = new DefaultConfigParamsImpl();
config.setCustomRaftPolicyImplementationClass(DisableElectionsRaftPolicy.class.getName());
RaftActorSnapshotCohort snapshotCohort = new RaftActorSnapshotCohort() {
@Override
public void createSnapshot(ActorRef actorRef, java.util.Optional<OutputStream> installSnapshotStream) {
actorRef.tell(new CaptureSnapshotReply(ByteState.empty(), installSnapshotStream), actorRef);
}
@Override
public void applySnapshot(Snapshot.State snapshotState) {
}
@Override
public State deserializeSnapshot(ByteSource snapshotBytes) {
throw new UnsupportedOperationException();
}
};
TestActorRef<MockRaftActor> raftActorRef = factory.createTestActor(MockRaftActor.builder().id(id).config(config).snapshotCohort(snapshotCohort).persistent(Optional.of(true)).props().withDispatcher(Dispatchers.DefaultDispatcherId()), id);
MockRaftActor mockRaftActor = raftActorRef.underlyingActor();
mockRaftActor.waitForRecoveryComplete();
Uninterruptibles.sleepUninterruptibly(750, TimeUnit.MILLISECONDS);
List<Snapshot> snapshots = InMemorySnapshotStore.getSnapshots(id, Snapshot.class);
assertEquals("Snapshots", 0, snapshots.size());
TEST_LOG.info("testNoSnapshotAfterStartupWithNoMigratedMessages ending");
}
use of org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshotReply in project controller by opendaylight.
the class RaftActorSnapshotMessageSupportTest method testOnCaptureSnapshotReply.
@Test
public void testOnCaptureSnapshotReply() {
ByteState state = ByteState.of(new byte[] { 1, 2, 3, 4, 5 });
Optional<OutputStream> optionalStream = Optional.of(mock(OutputStream.class));
sendMessageToSupport(new CaptureSnapshotReply(state, optionalStream));
verify(mockSnapshotManager).persist(eq(state), eq(optionalStream), anyLong());
}
use of org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshotReply in project controller by opendaylight.
the class RaftActorTest method testFakeSnapshotsForLeaderWithInInitiateSnapshots.
@Test
public void testFakeSnapshotsForLeaderWithInInitiateSnapshots() throws Exception {
final String persistenceId = factory.generateActorId("leader-");
final String follower1Id = factory.generateActorId("follower-");
final String follower2Id = factory.generateActorId("follower-");
final ActorRef followerActor1 = factory.createActor(MessageCollectorActor.props(), follower1Id);
final ActorRef followerActor2 = factory.createActor(MessageCollectorActor.props(), follower2Id);
DefaultConfigParamsImpl config = new DefaultConfigParamsImpl();
config.setHeartBeatInterval(new FiniteDuration(1, TimeUnit.DAYS));
config.setIsolatedLeaderCheckInterval(new FiniteDuration(1, TimeUnit.DAYS));
DataPersistenceProvider dataPersistenceProvider = mock(DataPersistenceProvider.class);
Map<String, String> peerAddresses = new HashMap<>();
peerAddresses.put(follower1Id, followerActor1.path().toString());
peerAddresses.put(follower2Id, followerActor2.path().toString());
TestActorRef<MockRaftActor> mockActorRef = factory.createTestActor(MockRaftActor.props(persistenceId, peerAddresses, config, dataPersistenceProvider), persistenceId);
MockRaftActor leaderActor = mockActorRef.underlyingActor();
leaderActor.getRaftActorContext().setCommitIndex(9);
leaderActor.getRaftActorContext().setLastApplied(9);
leaderActor.getRaftActorContext().getTermInformation().update(1, persistenceId);
leaderActor.waitForInitializeBehaviorComplete();
Leader leader = new Leader(leaderActor.getRaftActorContext());
leaderActor.setCurrentBehavior(leader);
assertEquals(RaftState.Leader, leaderActor.getCurrentBehavior().state());
// create 5 entries in the log
MockRaftActorContext.MockReplicatedLogBuilder logBuilder = new MockRaftActorContext.MockReplicatedLogBuilder();
leaderActor.getRaftActorContext().setReplicatedLog(logBuilder.createEntries(5, 10, 1).build());
// set the snapshot index to 4 , 0 to 4 are snapshotted
leaderActor.getRaftActorContext().getReplicatedLog().setSnapshotIndex(4);
// setting replicatedToAllIndex = 9, for the log to clear
leader.setReplicatedToAllIndex(9);
assertEquals(5, leaderActor.getReplicatedLog().size());
assertEquals(RaftState.Leader, leaderActor.getCurrentBehavior().state());
leaderActor.onReceiveCommand(new AppendEntriesReply(follower1Id, 1, true, 9, 1, (short) 0));
assertEquals(5, leaderActor.getReplicatedLog().size());
assertEquals(RaftState.Leader, leaderActor.getCurrentBehavior().state());
// set the 2nd follower nextIndex to 1 which has been snapshotted
leaderActor.onReceiveCommand(new AppendEntriesReply(follower2Id, 1, true, 0, 1, (short) 0));
assertEquals(5, leaderActor.getReplicatedLog().size());
assertEquals(RaftState.Leader, leaderActor.getCurrentBehavior().state());
// simulate a real snapshot
leaderActor.onReceiveCommand(SendHeartBeat.INSTANCE);
assertEquals(5, leaderActor.getReplicatedLog().size());
assertEquals(String.format("expected to be Leader but was %s. Current Leader = %s ", leaderActor.getCurrentBehavior().state(), leaderActor.getLeaderId()), RaftState.Leader, leaderActor.getCurrentBehavior().state());
// reply from a slow follower does not initiate a fake snapshot
leaderActor.onReceiveCommand(new AppendEntriesReply(follower2Id, 1, true, 9, 1, (short) 0));
assertEquals("Fake snapshot should not happen when Initiate is in progress", 5, leaderActor.getReplicatedLog().size());
ByteString snapshotBytes = fromObject(Arrays.asList(new MockRaftActorContext.MockPayload("foo-0"), new MockRaftActorContext.MockPayload("foo-1"), new MockRaftActorContext.MockPayload("foo-2"), new MockRaftActorContext.MockPayload("foo-3"), new MockRaftActorContext.MockPayload("foo-4")));
leaderActor.onReceiveCommand(new CaptureSnapshotReply(ByteState.of(snapshotBytes.toByteArray()), java.util.Optional.empty()));
assertTrue(leaderActor.getRaftActorContext().getSnapshotManager().isCapturing());
assertEquals("Real snapshot didn't clear the log till replicatedToAllIndex", 0, leaderActor.getReplicatedLog().size());
// reply from a slow follower after should not raise errors
leaderActor.onReceiveCommand(new AppendEntriesReply(follower2Id, 1, true, 5, 1, (short) 0));
assertEquals(0, leaderActor.getReplicatedLog().size());
}
use of org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshotReply in project controller by opendaylight.
the class RaftActorTest method testFakeSnapshotsForFollowerWithInRealSnapshots.
@Test
public void testFakeSnapshotsForFollowerWithInRealSnapshots() throws Exception {
final String persistenceId = factory.generateActorId("follower-");
final String leaderId = factory.generateActorId("leader-");
ActorRef leaderActor1 = factory.createActor(MessageCollectorActor.props());
DefaultConfigParamsImpl config = new DefaultConfigParamsImpl();
config.setHeartBeatInterval(new FiniteDuration(1, TimeUnit.DAYS));
config.setIsolatedLeaderCheckInterval(new FiniteDuration(1, TimeUnit.DAYS));
DataPersistenceProvider dataPersistenceProvider = mock(DataPersistenceProvider.class);
Map<String, String> peerAddresses = new HashMap<>();
peerAddresses.put(leaderId, leaderActor1.path().toString());
TestActorRef<MockRaftActor> mockActorRef = factory.createTestActor(MockRaftActor.props(persistenceId, peerAddresses, config, dataPersistenceProvider), persistenceId);
MockRaftActor followerActor = mockActorRef.underlyingActor();
followerActor.getRaftActorContext().setCommitIndex(4);
followerActor.getRaftActorContext().setLastApplied(4);
followerActor.getRaftActorContext().getTermInformation().update(1, persistenceId);
followerActor.waitForInitializeBehaviorComplete();
Follower follower = new Follower(followerActor.getRaftActorContext());
followerActor.setCurrentBehavior(follower);
assertEquals(RaftState.Follower, followerActor.getCurrentBehavior().state());
// create 6 entries in the log - 0 to 4 are applied and will get picked up as part of the capture snapshot
MockRaftActorContext.MockReplicatedLogBuilder logBuilder = new MockRaftActorContext.MockReplicatedLogBuilder();
followerActor.getRaftActorContext().setReplicatedLog(logBuilder.createEntries(0, 6, 1).build());
// log has indices 0-5
assertEquals(6, followerActor.getReplicatedLog().size());
// snapshot on 4
followerActor.getRaftActorContext().getSnapshotManager().capture(new SimpleReplicatedLogEntry(5, 1, new MockRaftActorContext.MockPayload("D")), 4);
verify(followerActor.snapshotCohortDelegate).createSnapshot(anyObject(), anyObject());
assertEquals(6, followerActor.getReplicatedLog().size());
// fake snapshot on index 6
List<ReplicatedLogEntry> entries = Arrays.asList((ReplicatedLogEntry) new SimpleReplicatedLogEntry(6, 1, new MockRaftActorContext.MockPayload("foo-6")));
followerActor.onReceiveCommand(new AppendEntries(1, leaderId, 5, 1, entries, 5, 5, (short) 0));
assertEquals(7, followerActor.getReplicatedLog().size());
// fake snapshot on index 7
assertEquals(RaftState.Follower, followerActor.getCurrentBehavior().state());
entries = Arrays.asList((ReplicatedLogEntry) new SimpleReplicatedLogEntry(7, 1, new MockRaftActorContext.MockPayload("foo-7")));
followerActor.onReceiveCommand(new AppendEntries(1, leaderId, 6, 1, entries, 6, 6, (short) 0));
assertEquals(8, followerActor.getReplicatedLog().size());
assertEquals(RaftState.Follower, followerActor.getCurrentBehavior().state());
ByteString snapshotBytes = fromObject(Arrays.asList(new MockRaftActorContext.MockPayload("foo-0"), new MockRaftActorContext.MockPayload("foo-1"), new MockRaftActorContext.MockPayload("foo-2"), new MockRaftActorContext.MockPayload("foo-3"), new MockRaftActorContext.MockPayload("foo-4")));
followerActor.onReceiveCommand(new CaptureSnapshotReply(ByteState.of(snapshotBytes.toByteArray()), java.util.Optional.empty()));
assertTrue(followerActor.getRaftActorContext().getSnapshotManager().isCapturing());
// The commit is needed to complete the snapshot creation process
followerActor.getRaftActorContext().getSnapshotManager().commit(-1, -1);
// capture snapshot reply should remove the snapshotted entries only till replicatedToAllIndex
// indexes 5,6,7 left in the log
assertEquals(3, followerActor.getReplicatedLog().size());
assertEquals(7, followerActor.getReplicatedLog().lastIndex());
entries = Arrays.asList((ReplicatedLogEntry) new SimpleReplicatedLogEntry(8, 1, new MockRaftActorContext.MockPayload("foo-7")));
// send an additional entry 8 with leaderCommit = 7
followerActor.onReceiveCommand(new AppendEntries(1, leaderId, 7, 1, entries, 7, 7, (short) 0));
// 7 and 8, as lastapplied is 7
assertEquals(2, followerActor.getReplicatedLog().size());
}
Aggregations