use of org.opendaylight.controller.cluster.raft.messages.AppendEntries in project controller by opendaylight.
the class FollowerTest method testHandleAppendEntriesSenderPrevLogTermNotSameAsReceiverPrevLogTerm.
/**
* This test verifies that when an AppendEntries is received a specific prevLogTerm
* which does not match the term that is in RaftActors log entry at prevLogIndex
* then the RaftActor does not change it's state and it returns a failure.
*/
@Test
public void testHandleAppendEntriesSenderPrevLogTermNotSameAsReceiverPrevLogTerm() {
logStart("testHandleAppendEntriesSenderPrevLogTermNotSameAsReceiverPrevLogTerm");
MockRaftActorContext context = createActorContext();
// First set the receivers term to lower number
context.getTermInformation().update(95, "test");
// AppendEntries is now sent with a bigger term
// this will set the receivers term to be the same as the sender's term
AppendEntries appendEntries = new AppendEntries(100, "leader", 0, 0, Collections.emptyList(), 101, -1, (short) 0);
follower = createBehavior(context);
RaftActorBehavior newBehavior = follower.handleMessage(leaderActor, appendEntries);
Assert.assertSame(follower, newBehavior);
AppendEntriesReply reply = MessageCollectorActor.expectFirstMatching(leaderActor, AppendEntriesReply.class);
assertEquals("isSuccess", false, reply.isSuccess());
}
use of org.opendaylight.controller.cluster.raft.messages.AppendEntries 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.messages.AppendEntries in project controller by opendaylight.
the class FollowerTest method testReceivingAppendEntriesDuringInstallSnapshotFromDifferentLeader.
@Test
public void testReceivingAppendEntriesDuringInstallSnapshotFromDifferentLeader() {
logStart("testReceivingAppendEntriesDuringInstallSnapshotFromDifferentLeader");
MockRaftActorContext context = createActorContext();
follower = createBehavior(context);
ByteString bsSnapshot = createSnapshot();
int snapshotLength = bsSnapshot.size();
int chunkSize = 50;
int totalChunks = snapshotLength / chunkSize + (snapshotLength % chunkSize > 0 ? 1 : 0);
int lastIncludedIndex = 1;
// Check that snapshot installation is not in progress
assertNull(follower.getSnapshotTracker());
// Make sure that we have more than 1 chunk to send
assertTrue(totalChunks > 1);
// Send an install snapshot with the first chunk to start the process of installing a snapshot
byte[] chunkData = getNextChunk(bsSnapshot, 0, chunkSize);
follower.handleMessage(leaderActor, new InstallSnapshot(1, "leader", lastIncludedIndex, 1, chunkData, 1, totalChunks));
// Check if snapshot installation is in progress now
assertNotNull(follower.getSnapshotTracker());
// Send appendEntries with a new term and leader.
AppendEntries appendEntries = new AppendEntries(2, "new-leader", 1, 1, Arrays.asList(newReplicatedLogEntry(2, 2, "3")), 2, -1, (short) 1);
follower.handleMessage(leaderActor, appendEntries);
AppendEntriesReply reply = MessageCollectorActor.expectFirstMatching(leaderActor, AppendEntriesReply.class);
assertEquals("isSuccess", true, reply.isSuccess());
assertEquals("getLogLastIndex", 2, reply.getLogLastIndex());
assertEquals("getLogLastTerm", 2, reply.getLogLastTerm());
assertEquals("getTerm", 2, reply.getTerm());
assertNull(follower.getSnapshotTracker());
}
use of org.opendaylight.controller.cluster.raft.messages.AppendEntries 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.messages.AppendEntries 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;
}
Aggregations