use of akka.japi.Procedure in project controller by opendaylight.
the class ElectionTermImplTest method testUpdateAndPersist.
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void testUpdateAndPersist() throws Exception {
ElectionTermImpl impl = new ElectionTermImpl(mockPersistence, "test", LOG);
impl.updateAndPersist(10, "member-1");
assertEquals("getCurrentTerm", 10, impl.getCurrentTerm());
assertEquals("getVotedFor", "member-1", impl.getVotedFor());
ArgumentCaptor<Object> message = ArgumentCaptor.forClass(Object.class);
ArgumentCaptor<Procedure> procedure = ArgumentCaptor.forClass(Procedure.class);
verify(mockPersistence).persist(message.capture(), procedure.capture());
assertEquals("Message type", UpdateElectionTerm.class, message.getValue().getClass());
UpdateElectionTerm update = (UpdateElectionTerm) message.getValue();
assertEquals("getCurrentTerm", 10, update.getCurrentTerm());
assertEquals("getVotedFor", "member-1", update.getVotedFor());
procedure.getValue().apply(null);
}
use of akka.japi.Procedure in project controller by opendaylight.
the class RaftActorTest method testReplicateWithPersistencePending.
@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void testReplicateWithPersistencePending() throws Exception {
final String leaderId = factory.generateActorId("leader-");
final String followerId = factory.generateActorId("follower-");
final ActorRef followerActor = factory.createActor(MessageCollectorActor.props());
DefaultConfigParamsImpl config = new DefaultConfigParamsImpl();
config.setHeartBeatInterval(new FiniteDuration(1, TimeUnit.DAYS));
config.setIsolatedLeaderCheckInterval(new FiniteDuration(1, TimeUnit.DAYS));
DataPersistenceProvider mockPersistenceProvider = mock(DataPersistenceProvider.class);
doReturn(true).when(mockPersistenceProvider).isRecoveryApplicable();
TestActorRef<MockRaftActor> leaderActorRef = factory.createTestActor(MockRaftActor.props(leaderId, ImmutableMap.of(followerId, followerActor.path().toString()), config, mockPersistenceProvider), leaderId);
MockRaftActor leaderActor = leaderActorRef.underlyingActor();
leaderActor.waitForInitializeBehaviorComplete();
leaderActor.getRaftActorContext().getTermInformation().update(1, leaderId);
Leader leader = new Leader(leaderActor.getRaftActorContext());
leaderActor.setCurrentBehavior(leader);
leaderActor.persistData(leaderActorRef, new MockIdentifier("1"), new MockRaftActorContext.MockPayload("1"), false);
ReplicatedLogEntry logEntry = leaderActor.getReplicatedLog().get(0);
assertNotNull("ReplicatedLogEntry not found", logEntry);
assertEquals("isPersistencePending", true, logEntry.isPersistencePending());
assertEquals("getCommitIndex", -1, leaderActor.getRaftActorContext().getCommitIndex());
leaderActor.onReceiveCommand(new AppendEntriesReply(followerId, 1, true, 0, 1, (short) 0));
assertEquals("getCommitIndex", -1, leaderActor.getRaftActorContext().getCommitIndex());
ArgumentCaptor<Procedure> callbackCaptor = ArgumentCaptor.forClass(Procedure.class);
verify(mockPersistenceProvider).persistAsync(eq(logEntry), callbackCaptor.capture());
callbackCaptor.getValue().apply(logEntry);
assertEquals("getCommitIndex", 0, leaderActor.getRaftActorContext().getCommitIndex());
assertEquals("getLastApplied", 0, leaderActor.getRaftActorContext().getLastApplied());
}
use of akka.japi.Procedure 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