Search in sources :

Example 1 with SimpleReplicatedLogEntry

use of org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry in project controller by opendaylight.

the class RaftActor method persistData.

/**
 * Persists the given Payload in the journal and replicates to any followers. After successful completion,
 * {@link #applyState(ActorRef, Identifier, Object)} is notified.
 *
 * @param clientActor optional ActorRef that is provided via the applyState callback
 * @param identifier the payload identifier
 * @param data the payload data to persist
 * @param batchHint if true, an attempt is made to delay immediate replication and batch the payload with
 *        subsequent payloads for efficiency. Otherwise the payload is immediately replicated.
 */
protected final void persistData(final ActorRef clientActor, final Identifier identifier, final Payload data, final boolean batchHint) {
    ReplicatedLogEntry replicatedLogEntry = new SimpleReplicatedLogEntry(context.getReplicatedLog().lastIndex() + 1, context.getTermInformation().getCurrentTerm(), data);
    replicatedLogEntry.setPersistencePending(true);
    LOG.debug("{}: Persist data {}", persistenceId(), replicatedLogEntry);
    final RaftActorContext raftContext = getRaftActorContext();
    boolean wasAppended = replicatedLog().appendAndPersist(replicatedLogEntry, persistedLogEntry -> {
        // Clear the persistence pending flag in the log entry.
        persistedLogEntry.setPersistencePending(false);
        if (!hasFollowers()) {
            // Increment the Commit Index and the Last Applied values
            raftContext.setCommitIndex(persistedLogEntry.getIndex());
            raftContext.setLastApplied(persistedLogEntry.getIndex());
            // Apply the state immediately.
            handleApplyState(new ApplyState(clientActor, identifier, persistedLogEntry));
            // Send a ApplyJournalEntries message so that we write the fact that we applied
            // the state to durable storage
            self().tell(new ApplyJournalEntries(persistedLogEntry.getIndex()), self());
        } else {
            context.getReplicatedLog().captureSnapshotIfReady(replicatedLogEntry);
            // Local persistence is complete so send the CheckConsensusReached message to the behavior (which
            // normally should still be the leader) to check if consensus has now been reached in conjunction with
            // follower replication.
            getCurrentBehavior().handleMessage(getSelf(), CheckConsensusReached.INSTANCE);
        }
    }, true);
    if (wasAppended && hasFollowers()) {
        // Send log entry for replication.
        getCurrentBehavior().handleMessage(getSelf(), new Replicate(clientActor, identifier, replicatedLogEntry, !batchHint));
    }
}
Also used : SimpleReplicatedLogEntry(org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry) Replicate(org.opendaylight.controller.cluster.raft.base.messages.Replicate) SimpleReplicatedLogEntry(org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry) ApplyJournalEntries(org.opendaylight.controller.cluster.raft.persisted.ApplyJournalEntries) ApplyState(org.opendaylight.controller.cluster.raft.base.messages.ApplyState)

Example 2 with SimpleReplicatedLogEntry

use of org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry in project controller by opendaylight.

the class ShardTest method testDataTreeCandidateRecovery.

@Test
public void testDataTreeCandidateRecovery() throws Exception {
    // Set up the InMemorySnapshotStore.
    final DataTree source = setupInMemorySnapshotStore();
    final DataTreeModification writeMod = source.takeSnapshot().newModification();
    writeMod.write(TestModel.OUTER_LIST_PATH, ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build());
    writeMod.ready();
    InMemoryJournal.addEntry(shardID.toString(), 0, DUMMY_DATA);
    // Set up the InMemoryJournal.
    InMemoryJournal.addEntry(shardID.toString(), 1, new SimpleReplicatedLogEntry(0, 1, payloadForModification(source, writeMod, nextTransactionId())));
    final int nListEntries = 16;
    final Set<Integer> listEntryKeys = new HashSet<>();
    // Add some ModificationPayload entries
    for (int i = 1; i <= nListEntries; i++) {
        listEntryKeys.add(Integer.valueOf(i));
        final YangInstanceIdentifier path = YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH).nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, i).build();
        final DataTreeModification mod = source.takeSnapshot().newModification();
        mod.merge(path, ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, i));
        mod.ready();
        InMemoryJournal.addEntry(shardID.toString(), i + 1, new SimpleReplicatedLogEntry(i, 1, payloadForModification(source, mod, nextTransactionId())));
    }
    InMemoryJournal.addEntry(shardID.toString(), nListEntries + 2, new ApplyJournalEntries(nListEntries));
    testRecovery(listEntryKeys);
}
Also used : DataTreeModification(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification) DataTree(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree) SimpleReplicatedLogEntry(org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry) ApplyJournalEntries(org.opendaylight.controller.cluster.raft.persisted.ApplyJournalEntries) YangInstanceIdentifier(org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier) HashSet(java.util.HashSet) Test(org.junit.Test)

Example 3 with SimpleReplicatedLogEntry

use of org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry 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 4 with SimpleReplicatedLogEntry

use of org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry in project controller by opendaylight.

the class LeaderTest method testHandleReplicateMessageWhenThereAreNoFollowers.

@Test
public void testHandleReplicateMessageWhenThereAreNoFollowers() throws Exception {
    logStart("testHandleReplicateMessageWhenThereAreNoFollowers");
    MockRaftActorContext actorContext = createActorContext();
    leader = new Leader(actorContext);
    actorContext.setLastApplied(0);
    long newLogIndex = actorContext.getReplicatedLog().lastIndex() + 1;
    long term = actorContext.getTermInformation().getCurrentTerm();
    ReplicatedLogEntry newEntry = new SimpleReplicatedLogEntry(newLogIndex, term, new MockRaftActorContext.MockPayload("foo"));
    actorContext.getReplicatedLog().append(newEntry);
    final Identifier id = new MockIdentifier("state-id");
    RaftActorBehavior raftBehavior = leader.handleMessage(leaderActor, new Replicate(leaderActor, id, newEntry, true));
    // State should not change
    assertTrue(raftBehavior instanceof Leader);
    assertEquals("getCommitIndex", newLogIndex, actorContext.getCommitIndex());
    // We should get 2 ApplyState messages - 1 for new log entry and 1 for the previous
    // one since lastApplied state is 0.
    List<ApplyState> applyStateList = MessageCollectorActor.getAllMatching(leaderActor, ApplyState.class);
    assertEquals("ApplyState count", newLogIndex, applyStateList.size());
    for (int i = 0; i <= newLogIndex - 1; i++) {
        ApplyState applyState = applyStateList.get(i);
        assertEquals("getIndex", i + 1, applyState.getReplicatedLogEntry().getIndex());
        assertEquals("getTerm", term, applyState.getReplicatedLogEntry().getTerm());
    }
    ApplyState last = applyStateList.get((int) newLogIndex - 1);
    assertEquals("getData", newEntry.getData(), last.getReplicatedLogEntry().getData());
    assertEquals("getIdentifier", id, last.getIdentifier());
}
Also used : MockRaftActorContext(org.opendaylight.controller.cluster.raft.MockRaftActorContext) SimpleReplicatedLogEntry(org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry) ReplicatedLogEntry(org.opendaylight.controller.cluster.raft.ReplicatedLogEntry) Identifier(org.opendaylight.yangtools.concepts.Identifier) Replicate(org.opendaylight.controller.cluster.raft.base.messages.Replicate) SimpleReplicatedLogEntry(org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry) ApplyState(org.opendaylight.controller.cluster.raft.base.messages.ApplyState) Test(org.junit.Test)

Example 5 with SimpleReplicatedLogEntry

use of org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry in project controller by opendaylight.

the class PartitionedCandidateOnStartupElectionScenarioTest method setupPartitionedCandidateMember3AndSendElectionTimeouts.

private void setupPartitionedCandidateMember3AndSendElectionTimeouts() {
    testLog.info("setupPartitionedCandidateMember3AndSendElectionTimeouts starting");
    // Create member 3's behavior initially as a Candidate.
    member3Context = newRaftActorContext("member3", member3ActorRef, ImmutableMap.<String, String>builder().put("member1", member1ActorRef.path().toString()).put("member2", member2ActorRef.path().toString()).build());
    DefaultConfigParamsImpl member3ConfigParams = newConfigParams();
    member3Context.setConfigParams(member3ConfigParams);
    // Initialize the ReplicatedLog and election term info for Candidate member 3. The current term
    // will be 2 and the last term will be 1 so it is behind the leader's log.
    SimpleReplicatedLog candidateReplicatedLog = new SimpleReplicatedLog();
    candidateReplicatedLog.append(new SimpleReplicatedLogEntry(0, 2, new MockPayload("")));
    member3Context.setReplicatedLog(candidateReplicatedLog);
    member3Context.setCommitIndex(candidateReplicatedLog.lastIndex());
    member3Context.setLastApplied(candidateReplicatedLog.lastIndex());
    member3Context.getTermInformation().update(2, member1Context.getId());
    // The member 3 Candidate will start a new term and send RequestVotes. However it will be
    // partitioned from the cluster by having member 1 and 2 drop its RequestVote messages.
    candidateElectionTerm = member3Context.getTermInformation().getCurrentTerm() + numCandidateElections;
    member1Actor.dropMessagesToBehavior(RequestVote.class, numCandidateElections);
    member2Actor.dropMessagesToBehavior(RequestVote.class, numCandidateElections);
    member3Actor.self().tell(new SetBehavior(new Candidate(member3Context), member3Context), ActorRef.noSender());
    for (int i = 0; i < numCandidateElections - 1; i++) {
        member3ActorRef.tell(ElectionTimeout.INSTANCE, ActorRef.noSender());
    }
    member1Actor.waitForExpectedMessages(RequestVote.class);
    member2Actor.waitForExpectedMessages(RequestVote.class);
    verifyBehaviorState("member 1", member1Actor, RaftState.Leader);
    verifyBehaviorState("member 2", member2Actor, RaftState.Follower);
    verifyBehaviorState("member 3", member3Actor, RaftState.Candidate);
    assertEquals("member 1 election term", 3, member1Context.getTermInformation().getCurrentTerm());
    assertEquals("member 2 election term", 3, member2Context.getTermInformation().getCurrentTerm());
    assertEquals("member 3 election term", candidateElectionTerm, member3Context.getTermInformation().getCurrentTerm());
    testLog.info("setupPartitionedCandidateMember3AndSendElectionTimeouts ending");
}
Also used : SimpleReplicatedLog(org.opendaylight.controller.cluster.raft.MockRaftActorContext.SimpleReplicatedLog) SimpleReplicatedLogEntry(org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry) DefaultConfigParamsImpl(org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl) MockPayload(org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockPayload)

Aggregations

SimpleReplicatedLogEntry (org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry)75 Test (org.junit.Test)64 MockPayload (org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockPayload)32 Snapshot (org.opendaylight.controller.cluster.raft.persisted.Snapshot)17 ApplyJournalEntries (org.opendaylight.controller.cluster.raft.persisted.ApplyJournalEntries)15 UpdateElectionTerm (org.opendaylight.controller.cluster.raft.persisted.UpdateElectionTerm)12 FiniteDuration (scala.concurrent.duration.FiniteDuration)12 ByteString (com.google.protobuf.ByteString)11 ApplyState (org.opendaylight.controller.cluster.raft.base.messages.ApplyState)11 CaptureSnapshot (org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot)11 ServerConfigurationPayload (org.opendaylight.controller.cluster.raft.persisted.ServerConfigurationPayload)11 ServerInfo (org.opendaylight.controller.cluster.raft.persisted.ServerInfo)11 ActorRef (akka.actor.ActorRef)10 TestActorRef (akka.testkit.TestActorRef)9 ApplySnapshot (org.opendaylight.controller.cluster.raft.base.messages.ApplySnapshot)8 ByteState (org.opendaylight.controller.cluster.raft.persisted.ByteState)8 ArrayList (java.util.ArrayList)7 MockRaftActorContext (org.opendaylight.controller.cluster.raft.MockRaftActorContext)7 CaptureSnapshotReply (org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshotReply)7 DisableElectionsRaftPolicy (org.opendaylight.controller.cluster.raft.policy.DisableElectionsRaftPolicy)7