Search in sources :

Example 1 with CaptureSnapshot

use of org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot in project controller by opendaylight.

the class RaftActorSnapshotMessageSupport method onGetSnapshot.

private void onGetSnapshot(ActorRef sender) {
    log.debug("{}: onGetSnapshot", context.getId());
    if (context.getPersistenceProvider().isRecoveryApplicable()) {
        CaptureSnapshot captureSnapshot = context.getSnapshotManager().newCaptureSnapshot(context.getReplicatedLog().last(), -1);
        ActorRef snapshotReplyActor = context.actorOf(GetSnapshotReplyActor.props(captureSnapshot, ImmutableElectionTerm.copyOf(context.getTermInformation()), sender, snapshotReplyActorTimeout, context.getId(), context.getPeerServerInfo(true)));
        cohort.createSnapshot(snapshotReplyActor, Optional.empty());
    } else {
        Snapshot snapshot = Snapshot.create(EmptyState.INSTANCE, Collections.<ReplicatedLogEntry>emptyList(), -1, -1, -1, -1, context.getTermInformation().getCurrentTerm(), context.getTermInformation().getVotedFor(), context.getPeerServerInfo(true));
        sender.tell(new GetSnapshotReply(context.getId(), snapshot), context.getActor());
    }
}
Also used : CaptureSnapshot(org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot) Snapshot(org.opendaylight.controller.cluster.raft.persisted.Snapshot) ApplySnapshot(org.opendaylight.controller.cluster.raft.base.messages.ApplySnapshot) GetSnapshot(org.opendaylight.controller.cluster.raft.client.messages.GetSnapshot) ActorRef(akka.actor.ActorRef) CaptureSnapshot(org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot) GetSnapshotReply(org.opendaylight.controller.cluster.raft.client.messages.GetSnapshotReply)

Example 2 with CaptureSnapshot

use of org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot in project controller by opendaylight.

the class LeaderTest method testInitiateForceInstallSnapshot.

@Test
public void testInitiateForceInstallSnapshot() throws Exception {
    logStart("testInitiateForceInstallSnapshot");
    MockRaftActorContext actorContext = createActorContextWithFollower();
    final int followersLastIndex = 2;
    final int snapshotIndex = -1;
    final int newEntryIndex = 4;
    final int snapshotTerm = -1;
    final int currentTerm = 2;
    // set the snapshot variables in replicatedlog
    actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
    actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
    actorContext.setLastApplied(3);
    actorContext.setCommitIndex(followersLastIndex);
    actorContext.getReplicatedLog().removeFrom(0);
    AtomicReference<java.util.Optional<OutputStream>> installSnapshotStream = new AtomicReference<>();
    actorContext.setCreateSnapshotProcedure(installSnapshotStream::set);
    leader = new Leader(actorContext);
    actorContext.setCurrentBehavior(leader);
    // Leader will send an immediate heartbeat - ignore it.
    MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
    // set the snapshot as absent and check if capture-snapshot is invoked.
    leader.setSnapshotHolder(null);
    for (int i = 0; i < 4; i++) {
        actorContext.getReplicatedLog().append(new SimpleReplicatedLogEntry(i, 1, new MockRaftActorContext.MockPayload("X" + i)));
    }
    // new entry
    SimpleReplicatedLogEntry entry = new SimpleReplicatedLogEntry(newEntryIndex, currentTerm, new MockRaftActorContext.MockPayload("D"));
    actorContext.getReplicatedLog().append(entry);
    // update follower timestamp
    leader.markFollowerActive(FOLLOWER_ID);
    // Sending this AppendEntriesReply forces the Leader to capture a snapshot, which subsequently gets
    // installed with a SendInstallSnapshot
    leader.handleMessage(leaderActor, new AppendEntriesReply(FOLLOWER_ID, 1, false, 1, 1, (short) 1, true));
    assertEquals("isCapturing", true, actorContext.getSnapshotManager().isCapturing());
    CaptureSnapshot cs = actorContext.getSnapshotManager().getCaptureSnapshot();
    assertEquals(3, cs.getLastAppliedIndex());
    assertEquals(1, cs.getLastAppliedTerm());
    assertEquals(4, cs.getLastIndex());
    assertEquals(2, cs.getLastTerm());
    assertNotNull("Create snapshot procedure not invoked", installSnapshotStream.get());
    assertTrue("Install snapshot stream present", installSnapshotStream.get().isPresent());
    MessageCollectorActor.clearMessages(followerActor);
    // Sending Replicate message should not initiate another capture since the first is in progress.
    leader.handleMessage(leaderActor, new Replicate(null, new MockIdentifier("state-id"), entry, true));
    assertSame("CaptureSnapshot instance", cs, actorContext.getSnapshotManager().getCaptureSnapshot());
    // Similarly sending another AppendEntriesReply to force a snapshot should not initiate another capture.
    leader.handleMessage(leaderActor, new AppendEntriesReply(FOLLOWER_ID, 1, false, 1, 1, (short) 1, true));
    assertSame("CaptureSnapshot instance", cs, actorContext.getSnapshotManager().getCaptureSnapshot());
    // Now simulate the CaptureSnapshotReply to initiate snapshot install - the first chunk should be sent.
    final byte[] bytes = new byte[] { 1, 2, 3 };
    installSnapshotStream.get().get().write(bytes);
    actorContext.getSnapshotManager().persist(ByteState.of(bytes), installSnapshotStream.get(), Runtime.getRuntime().totalMemory());
    MessageCollectorActor.expectFirstMatching(followerActor, InstallSnapshot.class);
    // Sending another AppendEntriesReply to force a snapshot should be a no-op and not try to re-send the chunk.
    MessageCollectorActor.clearMessages(followerActor);
    leader.handleMessage(leaderActor, new AppendEntriesReply(FOLLOWER_ID, 1, false, 1, 1, (short) 1, true));
    MessageCollectorActor.assertNoneMatching(followerActor, InstallSnapshot.class, 200);
}
Also used : Optional(com.google.common.base.Optional) MockRaftActorContext(org.opendaylight.controller.cluster.raft.MockRaftActorContext) AtomicReference(java.util.concurrent.atomic.AtomicReference) Replicate(org.opendaylight.controller.cluster.raft.base.messages.Replicate) SimpleReplicatedLogEntry(org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry) AppendEntriesReply(org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply) CaptureSnapshot(org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot) Test(org.junit.Test)

Example 3 with CaptureSnapshot

use of org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot in project controller by opendaylight.

the class ReplicationAndSnapshotsWithLaggingFollowerIntegrationTest method verifyNoSubsequentSnapshotAfterMemoryThresholdExceededSnapshot.

/**
 * Send another payload to verify another snapshot is not done since the last snapshot trimmed the
 * first log entry so the memory threshold should not be exceeded.
 */
private void verifyNoSubsequentSnapshotAfterMemoryThresholdExceededSnapshot() throws Exception {
    ApplyState applyState;
    CaptureSnapshot captureSnapshot;
    MockPayload payload3 = sendPayloadData(leaderActor, "three");
    // Verify the leader applies the state.
    applyState = MessageCollectorActor.expectFirstMatching(leaderCollectorActor, ApplyState.class);
    verifyApplyState(applyState, leaderCollectorActor, payload3.toString(), currentTerm, 3, payload3);
    captureSnapshot = MessageCollectorActor.getFirstMatching(leaderCollectorActor, CaptureSnapshot.class);
    Assert.assertNull("Leader received unexpected CaptureSnapshot", captureSnapshot);
    // Verify the follower 1 applies the state.
    applyState = MessageCollectorActor.expectFirstMatching(follower1CollectorActor, ApplyState.class);
    verifyApplyState(applyState, null, null, currentTerm, 3, payload3);
    // Verify the follower 2 applies the state.
    applyState = MessageCollectorActor.expectFirstMatching(follower2CollectorActor, ApplyState.class);
    verifyApplyState(applyState, null, null, currentTerm, 3, payload3);
    // Verify the leader's state.
    verifyLeadersTrimmedLog(3);
    // Verify follower 1's state.
    verifyFollowersTrimmedLog(1, follower1Actor, 3);
    // Verify follower 2's state.
    verifyFollowersTrimmedLog(2, follower2Actor, 3);
    // Revert back to JVM total memory.
    leaderActor.underlyingActor().setMockTotalMemory(0);
    MessageCollectorActor.clearMessages(leaderCollectorActor);
    MessageCollectorActor.clearMessages(follower1CollectorActor);
    MessageCollectorActor.clearMessages(follower2CollectorActor);
    expSnapshotState.add(payload3);
}
Also used : CaptureSnapshot(org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot) ApplyState(org.opendaylight.controller.cluster.raft.base.messages.ApplyState) MockPayload(org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockPayload)

Example 4 with CaptureSnapshot

use of org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot in project controller by opendaylight.

the class ReplicationAndSnapshotsWithLaggingFollowerIntegrationTest method testLeaderSnapshotTriggeredByMemoryThresholdExceededWithLaggingFollower.

/**
 * Send payloads with follower 2 lagging with the last payload having a large enough size to trigger a
 * leader snapshot such that the leader trims its log from the last applied index.. Follower 2's log will
 * be behind by several entries and, when it is resumed, it should be caught up via a snapshot installed
 * by the leader.
 */
@Test
public void testLeaderSnapshotTriggeredByMemoryThresholdExceededWithLaggingFollower() throws Exception {
    testLog.info("testLeaderSnapshotTriggeredByMemoryThresholdExceededWithLaggingFollower starting");
    snapshotBatchCount = 5;
    setup();
    sendInitialPayloadsReplicatedToAllFollowers("zero");
    leaderActor.underlyingActor().setMockTotalMemory(1000);
    // We'll expect a ReplicatedLogImplEntry message and an ApplyJournalEntries message added to the journal.
    InMemoryJournal.addWriteMessagesCompleteLatch(leaderId, 2);
    follower2Actor.underlyingActor().startDropMessages(AppendEntries.class);
    // Sleep for at least the election timeout interval so follower 2 is deemed inactive by the leader.
    Uninterruptibles.sleepUninterruptibly(leaderConfigParams.getElectionTimeOutInterval().toMillis() + 5, TimeUnit.MILLISECONDS);
    // Send a payload with a large relative size but not enough to trigger a snapshot.
    MockPayload payload1 = sendPayloadData(leaderActor, "one", 500);
    // Verify the leader got consensus and applies the first log entry even though follower 2 didn't respond.
    List<ApplyState> applyStates = MessageCollectorActor.expectMatching(leaderCollectorActor, ApplyState.class, 1);
    verifyApplyState(applyStates.get(0), leaderCollectorActor, payload1.toString(), currentTerm, 1, payload1);
    // Wait for all the ReplicatedLogImplEntry and ApplyJournalEntries messages to be added to the journal
    // before the snapshot so the snapshot sequence # will be higher to ensure the snapshot gets
    // purged from the snapshot store after subsequent snapshots.
    InMemoryJournal.waitForWriteMessagesComplete(leaderId);
    // Verify a snapshot is not triggered.
    CaptureSnapshot captureSnapshot = MessageCollectorActor.getFirstMatching(leaderCollectorActor, CaptureSnapshot.class);
    Assert.assertNull("Leader received unexpected CaptureSnapshot", captureSnapshot);
    expSnapshotState.add(payload1);
    // Sleep for at least the election timeout interval so follower 2 is deemed inactive by the leader.
    Uninterruptibles.sleepUninterruptibly(leaderConfigParams.getElectionTimeOutInterval().toMillis() + 5, TimeUnit.MILLISECONDS);
    // Send another payload with a large enough relative size in combination with the last payload
    // that exceeds the memory threshold (70% * 1000 = 700) - this should do a snapshot.
    MockPayload payload2 = sendPayloadData(leaderActor, "two", 201);
    // Verify the leader applies the last log entry.
    applyStates = MessageCollectorActor.expectMatching(leaderCollectorActor, ApplyState.class, 2);
    verifyApplyState(applyStates.get(1), leaderCollectorActor, payload2.toString(), currentTerm, 2, payload2);
    // Verify follower 1 applies each log entry.
    applyStates = MessageCollectorActor.expectMatching(follower1CollectorActor, ApplyState.class, 2);
    verifyApplyState(applyStates.get(0), null, null, currentTerm, 1, payload1);
    verifyApplyState(applyStates.get(1), null, null, currentTerm, 2, payload2);
    // A snapshot should've occurred - wait for it to complete.
    MessageCollectorActor.expectFirstMatching(leaderCollectorActor, SaveSnapshotSuccess.class);
    // Because the snapshot was triggered by exceeding the memory threshold the leader should've advanced
    // the snapshot index to the last applied index and trimmed the log even though the entries weren't
    // replicated to all followers.
    verifyLeadersTrimmedLog(2, 0);
    // Verify the leader's persisted snapshot.
    List<Snapshot> persistedSnapshots = InMemorySnapshotStore.getSnapshots(leaderId, Snapshot.class);
    assertEquals("Persisted snapshots size", 1, persistedSnapshots.size());
    verifySnapshot("Persisted", persistedSnapshots.get(0), currentTerm, 1, currentTerm, 2);
    List<ReplicatedLogEntry> unAppliedEntry = persistedSnapshots.get(0).getUnAppliedEntries();
    assertEquals("Persisted Snapshot getUnAppliedEntries size", 1, unAppliedEntry.size());
    verifyReplicatedLogEntry(unAppliedEntry.get(0), currentTerm, 2, payload2);
    expSnapshotState.add(payload2);
    verifyInstallSnapshotToLaggingFollower(2L, null);
    // Sends a payload with index 3.
    verifyNoSubsequentSnapshotAfterMemoryThresholdExceededSnapshot();
    // Sends 3 payloads with indexes 4, 5 and 6.
    long leadersSnapshotIndexOnRecovery = verifyReplicationsAndSnapshotWithNoLaggingAfterInstallSnapshot();
    // Recover the leader from persistence and verify.
    long leadersLastIndexOnRecovery = 6;
    long leadersFirstJournalEntryIndexOnRecovery = leadersSnapshotIndexOnRecovery + 1;
    verifyLeaderRecoveryAfterReinstatement(leadersLastIndexOnRecovery, leadersSnapshotIndexOnRecovery, leadersFirstJournalEntryIndexOnRecovery);
    testLog.info("testLeaderSnapshotTriggeredByMemoryThresholdExceeded ending");
}
Also used : CaptureSnapshot(org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot) ApplySnapshot(org.opendaylight.controller.cluster.raft.base.messages.ApplySnapshot) Snapshot(org.opendaylight.controller.cluster.raft.persisted.Snapshot) InstallSnapshot(org.opendaylight.controller.cluster.raft.messages.InstallSnapshot) SimpleReplicatedLogEntry(org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry) CaptureSnapshot(org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot) MockPayload(org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockPayload) ApplyState(org.opendaylight.controller.cluster.raft.base.messages.ApplyState) Test(org.junit.Test)

Example 5 with CaptureSnapshot

use of org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot in project controller by opendaylight.

the class SnapshotManagerTest method testCaptureWithNullLastLogEntry.

@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void testCaptureWithNullLastLogEntry() throws Exception {
    boolean capture = snapshotManager.capture(null, 1);
    assertTrue(capture);
    assertEquals(true, snapshotManager.isCapturing());
    ArgumentCaptor<Optional> outputStream = ArgumentCaptor.forClass(Optional.class);
    verify(mockProcedure).accept(outputStream.capture());
    assertEquals("isPresent", false, outputStream.getValue().isPresent());
    CaptureSnapshot captureSnapshot = snapshotManager.getCaptureSnapshot();
    // LastIndex and LastTerm are picked up from the lastLogEntry
    assertEquals(-1L, captureSnapshot.getLastIndex());
    assertEquals(-1L, captureSnapshot.getLastTerm());
    // Since the actor does not have any followers (no peer addresses) lastApplied will be from lastLogEntry
    assertEquals(-1L, captureSnapshot.getLastAppliedIndex());
    assertEquals(-1L, captureSnapshot.getLastAppliedTerm());
    // 
    assertEquals(-1L, captureSnapshot.getReplicatedToAllIndex());
    assertEquals(-1L, captureSnapshot.getReplicatedToAllTerm());
    MessageCollectorActor.clearMessages(actorRef);
}
Also used : Optional(java.util.Optional) CaptureSnapshot(org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot) Test(org.junit.Test)

Aggregations

CaptureSnapshot (org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot)8 Test (org.junit.Test)6 SimpleReplicatedLogEntry (org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry)5 Optional (java.util.Optional)3 MockRaftActorContext (org.opendaylight.controller.cluster.raft.MockRaftActorContext)2 MockPayload (org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockPayload)2 ApplySnapshot (org.opendaylight.controller.cluster.raft.base.messages.ApplySnapshot)2 ApplyState (org.opendaylight.controller.cluster.raft.base.messages.ApplyState)2 Replicate (org.opendaylight.controller.cluster.raft.base.messages.Replicate)2 Snapshot (org.opendaylight.controller.cluster.raft.persisted.Snapshot)2 ActorRef (akka.actor.ActorRef)1 Optional (com.google.common.base.Optional)1 AtomicReference (java.util.concurrent.atomic.AtomicReference)1 GetSnapshot (org.opendaylight.controller.cluster.raft.client.messages.GetSnapshot)1 GetSnapshotReply (org.opendaylight.controller.cluster.raft.client.messages.GetSnapshotReply)1 AppendEntriesReply (org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply)1 InstallSnapshot (org.opendaylight.controller.cluster.raft.messages.InstallSnapshot)1