use of org.opendaylight.controller.cluster.raft.base.messages.Replicate in project controller by opendaylight.
the class AbstractLeader method handleMessage.
@Override
public RaftActorBehavior handleMessage(final ActorRef sender, final Object message) {
Preconditions.checkNotNull(sender, "sender should not be null");
if (appendEntriesMessageSlicer.handleMessage(message)) {
return this;
}
if (message instanceof RaftRPC) {
RaftRPC rpc = (RaftRPC) message;
// This applies to all RPC messages and responses
if (rpc.getTerm() > context.getTermInformation().getCurrentTerm()) {
log.info("{}: Term {} in \"{}\" message is greater than leader's term {} - switching to Follower", logName(), rpc.getTerm(), rpc, context.getTermInformation().getCurrentTerm());
context.getTermInformation().updateAndPersist(rpc.getTerm(), null);
// leadership, we should make every effort to get the requesting node elected.
if (message instanceof RequestVote && context.getRaftActorLeadershipTransferCohort() != null) {
log.debug("{}: Leadership transfer in progress - processing RequestVote", logName());
super.handleMessage(sender, message);
}
return internalSwitchBehavior(RaftState.Follower);
}
}
if (message instanceof SendHeartBeat) {
beforeSendHeartbeat();
sendHeartBeat();
scheduleHeartBeat(context.getConfigParams().getHeartBeatInterval());
} else if (message instanceof SendInstallSnapshot) {
SendInstallSnapshot sendInstallSnapshot = (SendInstallSnapshot) message;
setSnapshotHolder(new SnapshotHolder(sendInstallSnapshot.getSnapshot(), sendInstallSnapshot.getSnapshotBytes()));
sendInstallSnapshot();
} else if (message instanceof Replicate) {
replicate((Replicate) message);
} else if (message instanceof InstallSnapshotReply) {
handleInstallSnapshotReply((InstallSnapshotReply) message);
} else if (message instanceof CheckConsensusReached) {
possiblyUpdateCommitIndex();
} else {
return super.handleMessage(sender, message);
}
return this;
}
use of org.opendaylight.controller.cluster.raft.base.messages.Replicate 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));
}
}
use of org.opendaylight.controller.cluster.raft.base.messages.Replicate 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);
}
use of org.opendaylight.controller.cluster.raft.base.messages.Replicate 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());
}
use of org.opendaylight.controller.cluster.raft.base.messages.Replicate in project controller by opendaylight.
the class LeaderTest method testSendAppendEntriesSnapshotScenario.
@Test
public void testSendAppendEntriesSnapshotScenario() throws Exception {
logStart("testSendAppendEntriesSnapshotScenario");
final MockRaftActorContext actorContext = createActorContextWithFollower();
Map<String, String> leadersSnapshot = new HashMap<>();
leadersSnapshot.put("1", "A");
leadersSnapshot.put("2", "B");
leadersSnapshot.put("3", "C");
// clears leaders log
actorContext.getReplicatedLog().removeFrom(0);
final int followersLastIndex = 2;
final int snapshotIndex = 3;
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.setCommitIndex(followersLastIndex);
leader = new Leader(actorContext);
// Leader will send an immediate heartbeat - ignore it.
MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
// new entry
SimpleReplicatedLogEntry entry = new SimpleReplicatedLogEntry(newEntryIndex, currentTerm, new MockRaftActorContext.MockPayload("D"));
actorContext.getReplicatedLog().append(entry);
// update follower timestamp
leader.markFollowerActive(FOLLOWER_ID);
// this should invoke a sendinstallsnapshot as followersLastIndex < snapshotIndex
RaftActorBehavior raftBehavior = leader.handleMessage(leaderActor, new Replicate(null, new MockIdentifier("state-id"), entry, true));
assertTrue(raftBehavior instanceof Leader);
assertEquals("isCapturing", true, actorContext.getSnapshotManager().isCapturing());
}
Aggregations