use of org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockPayload in project controller by opendaylight.
the class IsolationScenarioTest method testLeaderIsolationWithPriorUncommittedEntryAndMultipleConflictingEntries.
/**
* Isolates the leader with a payload entry that's replicated to all followers and committed on the leader but
* uncommitted on the followers. While isolated, the majority partition elects a new leader and both sides of the
* partition attempt to commit multiple entries independently. After isolation is removed, the entries will conflict
* and both sides should reconcile their logs appropriately.
*/
@Test
public void testLeaderIsolationWithPriorUncommittedEntryAndMultipleConflictingEntries() throws Exception {
testLog.info("testLeaderIsolationWithPriorUncommittedEntryAndMultipleConflictingEntries starting");
createRaftActors();
// Submit an initial payload that is committed/applied on all nodes.
final MockPayload payload0 = sendPayloadData(leaderActor, "zero");
verifyApplyJournalEntries(leaderCollectorActor, 0);
verifyApplyJournalEntries(follower1CollectorActor, 0);
verifyApplyJournalEntries(follower2CollectorActor, 0);
// Submit another payload that is replicated to all followers and committed on the leader but the leader is
// isolated before the entry is committed on the followers. To accomplish this we drop the AppendEntries
// with the updated leader commit index.
follower1Actor.underlyingActor().startDropMessages(AppendEntries.class, ae -> ae.getLeaderCommit() == 1);
follower2Actor.underlyingActor().startDropMessages(AppendEntries.class, ae -> ae.getLeaderCommit() == 1);
MockPayload payload1 = sendPayloadData(leaderActor, "one");
// Wait for the isolated leader to send AppendEntries to the followers with the new entry with index 1. This
// message is forwarded to the followers.
expectFirstMatching(follower1CollectorActor, AppendEntries.class, ae -> ae.getEntries().size() == 1 && ae.getEntries().get(0).getIndex() == 1 && ae.getEntries().get(0).getData().equals(payload1));
expectFirstMatching(follower2CollectorActor, AppendEntries.class, ae -> ae.getEntries().size() == 1 && ae.getEntries().get(0).getIndex() == 1 && ae.getEntries().get(0).getData().equals(payload1));
verifyApplyJournalEntries(leaderCollectorActor, 1);
isolateLeader();
// Send 3 payloads to the isolated leader so it has uncommitted log entries.
testLog.info("Sending 3 payloads to isolated leader");
sendPayloadData(leaderActor, "two");
sendPayloadData(leaderActor, "three");
sendPayloadData(leaderActor, "four");
// Wait for the isolated leader to send AppendEntries to follower1 for each new entry. Note the messages
// are collected but not forwarded to the follower RaftActor.
expectFirstMatching(follower1CollectorActor, AppendEntries.class, ae -> {
for (ReplicatedLogEntry e : ae.getEntries()) {
if (e.getIndex() == 4) {
return true;
}
}
return false;
});
// The leader should transition to IsolatedLeader.
expectFirstMatching(leaderNotifierActor, RoleChanged.class, rc -> rc.getNewRole().equals(RaftState.IsolatedLeader.name()));
forceElectionOnFollower1();
// Send 3 payloads to the new leader follower1 and verify they're replicated to follower2 and committed. Since
// the entry with index 1 from the previous term was uncommitted, the new leader should've also committed a
// NoopPayload entry with index 2 in the PreLeader state. Thus the new payload indices will start at 3.
testLog.info("Sending 3 payloads to new leader");
final MockPayload newLeaderPayload2 = sendPayloadData(follower1Actor, "two-new");
final MockPayload newLeaderPayload3 = sendPayloadData(follower1Actor, "three-new");
final MockPayload newLeaderPayload4 = sendPayloadData(follower1Actor, "four-new");
verifyApplyJournalEntries(follower1CollectorActor, 5);
verifyApplyJournalEntries(follower2CollectorActor, 5);
assertEquals("Follower 1 journal last term", currentTerm, follower1Context.getReplicatedLog().lastTerm());
assertEquals("Follower 1 journal last index", 5, follower1Context.getReplicatedLog().lastIndex());
assertEquals("Follower 1 commit index", 5, follower1Context.getCommitIndex());
verifyReplicatedLogEntry(follower1Context.getReplicatedLog().get(5), currentTerm, 5, newLeaderPayload4);
assertEquals("Follower 1 state", Lists.newArrayList(payload0, payload1, newLeaderPayload2, newLeaderPayload3, newLeaderPayload4), follower1Actor.underlyingActor().getState());
removeIsolation();
// Previous leader should switch to follower b/c it will receive either an AppendEntries or AppendEntriesReply
// with a higher term.
expectFirstMatching(leaderNotifierActor, RoleChanged.class, rc -> rc.getNewRole().equals(RaftState.Follower.name()));
// The previous leader has conflicting log entries starting at index 2 with different terms which should get
// replaced by the new leader's entries.
verifyApplyJournalEntries(leaderCollectorActor, 5);
verifyRaftState(leaderActor, raftState -> {
assertEquals("Prior leader journal last term", currentTerm, leaderContext.getReplicatedLog().lastTerm());
assertEquals("Prior leader journal last index", 5, leaderContext.getReplicatedLog().lastIndex());
assertEquals("Prior leader commit index", 5, leaderContext.getCommitIndex());
});
assertEquals("Prior leader state", Lists.newArrayList(payload0, payload1, newLeaderPayload2, newLeaderPayload3, newLeaderPayload4), leaderActor.underlyingActor().getState());
// Ensure the prior leader didn't apply any of its conflicting entries with term 1.
List<ApplyState> applyState = getAllMatching(leaderCollectorActor, ApplyState.class);
for (ApplyState as : applyState) {
if (as.getReplicatedLogEntry().getTerm() == 1) {
fail("Got unexpected ApplyState: " + as);
}
}
// The prior leader should not have needed a snapshot installed in order to get it synced.
assertNoneMatching(leaderCollectorActor, InstallSnapshot.class);
testLog.info("testLeaderIsolationWithPriorUncommittedEntryAndMultipleConflictingEntries ending");
}
use of org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockPayload in project controller by opendaylight.
the class RaftActorRecoverySupportTest method testOnSnapshotOffer.
@Test
public void testOnSnapshotOffer() {
ReplicatedLog replicatedLog = context.getReplicatedLog();
replicatedLog.append(new SimpleReplicatedLogEntry(1, 1, new MockRaftActorContext.MockPayload("1")));
replicatedLog.append(new SimpleReplicatedLogEntry(2, 1, new MockRaftActorContext.MockPayload("2")));
replicatedLog.append(new SimpleReplicatedLogEntry(3, 1, new MockRaftActorContext.MockPayload("3")));
ReplicatedLogEntry unAppliedEntry1 = new SimpleReplicatedLogEntry(4, 1, new MockRaftActorContext.MockPayload("4", 4));
ReplicatedLogEntry unAppliedEntry2 = new SimpleReplicatedLogEntry(5, 1, new MockRaftActorContext.MockPayload("5", 5));
long lastAppliedDuringSnapshotCapture = 3;
long lastIndexDuringSnapshotCapture = 5;
long electionTerm = 2;
String electionVotedFor = "member-2";
MockSnapshotState snapshotState = new MockSnapshotState(Arrays.asList(new MockPayload("1")));
Snapshot snapshot = Snapshot.create(snapshotState, Arrays.asList(unAppliedEntry1, unAppliedEntry2), lastIndexDuringSnapshotCapture, 1, lastAppliedDuringSnapshotCapture, 1, electionTerm, electionVotedFor, null);
SnapshotMetadata metadata = new SnapshotMetadata("test", 6, 12345);
SnapshotOffer snapshotOffer = new SnapshotOffer(metadata, snapshot);
sendMessageToSupport(snapshotOffer);
assertEquals("Journal log size", 2, context.getReplicatedLog().size());
assertEquals("Journal data size", 9, context.getReplicatedLog().dataSize());
assertEquals("Last index", lastIndexDuringSnapshotCapture, context.getReplicatedLog().lastIndex());
assertEquals("Last applied", lastAppliedDuringSnapshotCapture, context.getLastApplied());
assertEquals("Commit index", lastAppliedDuringSnapshotCapture, context.getCommitIndex());
assertEquals("Snapshot term", 1, context.getReplicatedLog().getSnapshotTerm());
assertEquals("Snapshot index", lastAppliedDuringSnapshotCapture, context.getReplicatedLog().getSnapshotIndex());
assertEquals("Election term", electionTerm, context.getTermInformation().getCurrentTerm());
assertEquals("Election votedFor", electionVotedFor, context.getTermInformation().getVotedFor());
assertFalse("Dynamic server configuration", context.isDynamicServerConfigurationInUse());
verify(mockCohort).applyRecoverySnapshot(snapshotState);
}
use of org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockPayload in project controller by opendaylight.
the class PartitionedCandidateOnStartupElectionScenarioTest method setupInitialMember1AndMember2Behaviors.
private void setupInitialMember1AndMember2Behaviors() {
testLog.info("setupInitialMember1AndMember2Behaviors starting");
// Initialize the ReplicatedLog and election term info for member 1 and 2. The current term
// will be 3 and the last term will be 2.
SimpleReplicatedLog replicatedLog = new SimpleReplicatedLog();
replicatedLog.append(new SimpleReplicatedLogEntry(0, 2, new MockPayload("")));
replicatedLog.append(new SimpleReplicatedLogEntry(1, 3, new MockPayload("")));
// Create member 2's behavior as Follower.
member2Context = newRaftActorContext("member2", member2ActorRef, ImmutableMap.<String, String>builder().put("member1", member1ActorRef.path().toString()).put("member3", member3ActorRef.path().toString()).build());
DefaultConfigParamsImpl member2ConfigParams = newConfigParams();
member2Context.setConfigParams(member2ConfigParams);
member2Context.setReplicatedLog(replicatedLog);
member2Context.setCommitIndex(replicatedLog.lastIndex());
member2Context.setLastApplied(replicatedLog.lastIndex());
member2Context.getTermInformation().update(3, "member1");
member2Actor.self().tell(new SetBehavior(new Follower(member2Context), member2Context), ActorRef.noSender());
// Create member 1's behavior as Leader.
member1Context = newRaftActorContext("member1", member1ActorRef, ImmutableMap.<String, String>builder().put("member2", member2ActorRef.path().toString()).put("member3", member3ActorRef.path().toString()).build());
DefaultConfigParamsImpl member1ConfigParams = newConfigParams();
member1Context.setConfigParams(member1ConfigParams);
member1Context.setReplicatedLog(replicatedLog);
member1Context.setCommitIndex(replicatedLog.lastIndex());
member1Context.setLastApplied(replicatedLog.lastIndex());
member1Context.getTermInformation().update(3, "member1");
initializeLeaderBehavior(member1Actor, member1Context, 1);
member2Actor.clear();
member3Actor.clear();
testLog.info("setupInitialMember1AndMember2Behaviors ending");
}
use of org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockPayload in project controller by opendaylight.
the class RaftActorTest method testReplicateWithBatchHint.
@Test
public void testReplicateWithBatchHint() 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));
TestActorRef<MockRaftActor> leaderActorRef = factory.createTestActor(MockRaftActor.props(leaderId, ImmutableMap.of(followerId, followerActor.path().toString()), config), leaderId);
MockRaftActor leaderActor = leaderActorRef.underlyingActor();
leaderActor.waitForInitializeBehaviorComplete();
leaderActor.getRaftActorContext().getTermInformation().update(1, leaderId);
Leader leader = new Leader(leaderActor.getRaftActorContext());
leaderActor.setCurrentBehavior(leader);
MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
MessageCollectorActor.clearMessages(followerActor);
leaderActor.onReceiveCommand(new AppendEntriesReply(followerId, 1, true, -1, -1, (short) 0));
leaderActor.persistData(leaderActorRef, new MockIdentifier("1"), new MockPayload("1"), true);
MessageCollectorActor.assertNoneMatching(followerActor, AppendEntries.class, 500);
leaderActor.persistData(leaderActorRef, new MockIdentifier("2"), new MockPayload("2"), true);
MessageCollectorActor.assertNoneMatching(followerActor, AppendEntries.class, 500);
leaderActor.persistData(leaderActorRef, new MockIdentifier("3"), new MockPayload("3"), false);
AppendEntries appendEntries = MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
assertEquals("AppendEntries size", 3, appendEntries.getEntries().size());
}
use of org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockPayload in project controller by opendaylight.
the class RaftActorTest method testRestoreFromSnapshotWithRecoveredData.
@Test
public void testRestoreFromSnapshotWithRecoveredData() throws Exception {
TEST_LOG.info("testRestoreFromSnapshotWithRecoveredData starting");
String persistenceId = factory.generateActorId("test-actor-");
DefaultConfigParamsImpl config = new DefaultConfigParamsImpl();
config.setCustomRaftPolicyImplementationClass(DisableElectionsRaftPolicy.class.getName());
List<MockPayload> state = Arrays.asList(new MockRaftActorContext.MockPayload("A"));
Snapshot snapshot = Snapshot.create(ByteState.of(fromObject(state).toByteArray()), Arrays.<ReplicatedLogEntry>asList(), 5, 2, 5, 2, 2, "member-1", null);
InMemoryJournal.addEntry(persistenceId, 1, new SimpleReplicatedLogEntry(0, 1, new MockRaftActorContext.MockPayload("B")));
TestActorRef<MockRaftActor> raftActorRef = factory.createTestActor(MockRaftActor.builder().id(persistenceId).config(config).restoreFromSnapshot(snapshot).props().withDispatcher(Dispatchers.DefaultDispatcherId()), persistenceId);
MockRaftActor mockRaftActor = raftActorRef.underlyingActor();
mockRaftActor.waitForRecoveryComplete();
Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS);
verify(mockRaftActor.snapshotCohortDelegate, never()).applySnapshot(any(Snapshot.State.class));
RaftActorContext context = mockRaftActor.getRaftActorContext();
assertEquals("Journal log size", 1, context.getReplicatedLog().size());
assertEquals("Last index", 0, context.getReplicatedLog().lastIndex());
assertEquals("Last applied", -1, context.getLastApplied());
assertEquals("Commit index", -1, context.getCommitIndex());
assertEquals("Current term", 0, context.getTermInformation().getCurrentTerm());
assertEquals("Voted for", null, context.getTermInformation().getVotedFor());
TEST_LOG.info("testRestoreFromSnapshotWithRecoveredData ending");
}
Aggregations