use of org.opendaylight.controller.cluster.raft.ReplicatedLogEntry in project controller by opendaylight.
the class FollowerTest method testHandleAppendEntriesPreviousLogEntryMissing.
@Test
public void testHandleAppendEntriesPreviousLogEntryMissing() {
logStart("testHandleAppendEntriesPreviousLogEntryMissing");
final MockRaftActorContext context = createActorContext();
// Prepare the receivers log
MockRaftActorContext.SimpleReplicatedLog log = new MockRaftActorContext.SimpleReplicatedLog();
log.append(newReplicatedLogEntry(1, 0, "zero"));
log.append(newReplicatedLogEntry(1, 1, "one"));
log.append(newReplicatedLogEntry(1, 2, "two"));
context.setReplicatedLog(log);
// Prepare the entries to be sent with AppendEntries
List<ReplicatedLogEntry> entries = new ArrayList<>();
entries.add(newReplicatedLogEntry(1, 4, "four"));
AppendEntries appendEntries = new AppendEntries(1, "leader", 3, 1, entries, 4, -1, (short) 0);
follower = createBehavior(context);
RaftActorBehavior newBehavior = follower.handleMessage(leaderActor, appendEntries);
Assert.assertSame(follower, newBehavior);
expectAndVerifyAppendEntriesReply(1, false, context.getId(), 1, 2);
}
use of org.opendaylight.controller.cluster.raft.ReplicatedLogEntry in project controller by opendaylight.
the class FollowerTest method testHandleFirstAppendEntriesWithPrevIndexMinusOneAndReplicatedToAllIndexPresentInSnapshot.
@Test
public void testHandleFirstAppendEntriesWithPrevIndexMinusOneAndReplicatedToAllIndexPresentInSnapshot() {
logStart("testHandleFirstAppendEntries");
MockRaftActorContext context = createActorContext();
context.getReplicatedLog().clear(0, 2);
context.getReplicatedLog().setSnapshotIndex(100);
List<ReplicatedLogEntry> entries = Arrays.asList(newReplicatedLogEntry(2, 101, "foo"));
// The new commitIndex is 101
AppendEntries appendEntries = new AppendEntries(2, "leader-1", -1, -1, entries, 101, 100, (short) 0);
follower = createBehavior(context);
follower.handleMessage(leaderActor, appendEntries);
FollowerInitialSyncUpStatus syncStatus = MessageCollectorActor.expectFirstMatching(followerActor, FollowerInitialSyncUpStatus.class);
AppendEntriesReply reply = MessageCollectorActor.expectFirstMatching(leaderActor, AppendEntriesReply.class);
assertFalse(syncStatus.isInitialSyncDone());
assertTrue("append entries reply should be true", reply.isSuccess());
}
use of org.opendaylight.controller.cluster.raft.ReplicatedLogEntry in project controller by opendaylight.
the class AbstractRaftActorBehavior method applyLogToStateMachine.
/**
* Applies the log entries up to the specified index that is known to be committed to the state machine.
*
* @param index the log index
*/
protected void applyLogToStateMachine(final long index) {
// Now maybe we apply to the state machine
for (long i = context.getLastApplied() + 1; i < index + 1; i++) {
ReplicatedLogEntry replicatedLogEntry = context.getReplicatedLog().get(i);
if (replicatedLogEntry != null) {
// Send a local message to the local RaftActor (it's derived class to be
// specific to apply the log to it's index)
final ApplyState applyState;
final ClientRequestTracker tracker = removeClientRequestTracker(i);
if (tracker != null) {
applyState = new ApplyState(tracker.getClientActor(), tracker.getIdentifier(), replicatedLogEntry);
} else {
applyState = new ApplyState(null, null, replicatedLogEntry);
}
log.debug("{}: Setting last applied to {}", logName(), i);
context.setLastApplied(i);
context.getApplyStateConsumer().accept(applyState);
} else {
// if one index is not present in the log, no point in looping
// around as the rest wont be present either
log.warn("{}: Missing index {} from log. Cannot apply state. Ignoring {} to {}", logName(), i, i, index);
break;
}
}
// send a message to persist a ApplyLogEntries marker message into akka's persistent journal
// will be used during recovery
// in case if the above code throws an error and this message is not sent, it would be fine
// as the append entries received later would initiate add this message to the journal
actor().tell(new ApplyJournalEntries(context.getLastApplied()), actor());
}
use of org.opendaylight.controller.cluster.raft.ReplicatedLogEntry in project controller by opendaylight.
the class Follower method processNewEntries.
private boolean processNewEntries(final AppendEntries appendEntries, final ActorRef sender) {
int numLogEntries = appendEntries.getEntries().size();
if (numLogEntries == 0) {
return true;
}
log.debug("{}: Number of entries to be appended = {}", logName(), numLogEntries);
long lastIndex = lastIndex();
int addEntriesFrom = 0;
// term), delete the existing entry and all that follow it (ยง5.3)
if (context.getReplicatedLog().size() > 0) {
// Find the entry up until the one that is not in the follower's log
for (int i = 0; i < numLogEntries; i++, addEntriesFrom++) {
ReplicatedLogEntry matchEntry = appendEntries.getEntries().get(i);
if (!isLogEntryPresent(matchEntry.getIndex())) {
// newEntry not found in the log
break;
}
long existingEntryTerm = getLogEntryTerm(matchEntry.getIndex());
log.debug("{}: matchEntry {} is present: existingEntryTerm: {}", logName(), matchEntry, existingEntryTerm);
// what the term was so we'll assume it matches.
if (existingEntryTerm == -1 || existingEntryTerm == matchEntry.getTerm()) {
continue;
}
if (!context.getRaftPolicy().applyModificationToStateBeforeConsensus()) {
log.info("{}: Removing entries from log starting at {}, commitIndex: {}, lastApplied: {}", logName(), matchEntry.getIndex(), context.getCommitIndex(), context.getLastApplied());
// been applied to the state yet.
if (matchEntry.getIndex() <= context.getLastApplied() || !context.getReplicatedLog().removeFromAndPersist(matchEntry.getIndex())) {
// Could not remove the entries - this means the matchEntry index must be in the
// snapshot and not the log. In this case the prior entries are part of the state
// so we must send back a reply to force a snapshot to completely re-sync the
// follower's log and state.
log.info("{}: Could not remove entries - sending reply to force snapshot", logName());
sender.tell(new AppendEntriesReply(context.getId(), currentTerm(), false, lastIndex, lastTerm(), context.getPayloadVersion(), true), actor());
return false;
}
break;
} else {
sender.tell(new AppendEntriesReply(context.getId(), currentTerm(), false, lastIndex, lastTerm(), context.getPayloadVersion(), true), actor());
return false;
}
}
}
lastIndex = lastIndex();
log.debug("{}: After cleanup, lastIndex: {}, entries to be added from: {}", logName(), lastIndex, addEntriesFrom);
// When persistence successfully completes for each new log entry appended, we need to determine if we
// should capture a snapshot to compact the persisted log. shouldCaptureSnapshot tracks whether or not
// one of the log entries has exceeded the log size threshold whereby a snapshot should be taken. However
// we don't initiate the snapshot at that log entry but rather after the last log entry has been persisted.
// This is done because subsequent log entries after the one that tripped the threshold may have been
// applied to the state already, as the persistence callback occurs async, and we want those entries
// purged from the persisted log as well.
final AtomicBoolean shouldCaptureSnapshot = new AtomicBoolean(false);
final Procedure<ReplicatedLogEntry> appendAndPersistCallback = logEntry -> {
final List<ReplicatedLogEntry> entries = appendEntries.getEntries();
final ReplicatedLogEntry lastEntryToAppend = entries.get(entries.size() - 1);
if (shouldCaptureSnapshot.get() && logEntry == lastEntryToAppend) {
context.getSnapshotManager().capture(context.getReplicatedLog().last(), getReplicatedToAllIndex());
}
};
// Append any new entries not already in the log
for (int i = addEntriesFrom; i < numLogEntries; i++) {
ReplicatedLogEntry entry = appendEntries.getEntries().get(i);
log.debug("{}: Append entry to log {}", logName(), entry.getData());
context.getReplicatedLog().appendAndPersist(entry, appendAndPersistCallback, false);
shouldCaptureSnapshot.compareAndSet(false, context.getReplicatedLog().shouldCaptureSnapshot(entry.getIndex()));
if (entry.getData() instanceof ServerConfigurationPayload) {
context.updatePeerIds((ServerConfigurationPayload) entry.getData());
}
}
log.debug("{}: Log size is now {}", logName(), context.getReplicatedLog().size());
return true;
}
use of org.opendaylight.controller.cluster.raft.ReplicatedLogEntry in project controller by opendaylight.
the class AppendEntriesTest method testSerialization.
@Test
public void testSerialization() {
ReplicatedLogEntry entry1 = new SimpleReplicatedLogEntry(1, 2, new MockPayload("payload1"));
ReplicatedLogEntry entry2 = new SimpleReplicatedLogEntry(3, 4, new MockPayload("payload2"));
short payloadVersion = 5;
AppendEntries expected = new AppendEntries(5L, "node1", 7L, 8L, Arrays.asList(entry1, entry2), 10L, -1, payloadVersion);
AppendEntries cloned = (AppendEntries) SerializationUtils.clone(expected);
verifyAppendEntries(expected, cloned);
}
Aggregations