Search in sources :

Example 61 with AppendEntries

use of org.opendaylight.controller.cluster.raft.messages.AppendEntries in project controller by opendaylight.

the class IsolationScenarioTest method testLeaderIsolationWithAllPriorEntriesCommitted.

/**
 * Isolates the leader after all initial payload entries have been committed and applied on all nodes. While
 * isolated, the majority partition elects a new leader and both sides of the partition attempt to commit one entry
 * independently. After isolation is removed, the entry will conflict and both sides should reconcile their logs
 * appropriately.
 */
@Test
public void testLeaderIsolationWithAllPriorEntriesCommitted() throws Exception {
    testLog.info("testLeaderIsolationWithAllPriorEntriesCommitted starting");
    createRaftActors();
    // Send an initial payloads and verify replication.
    final MockPayload payload0 = sendPayloadData(leaderActor, "zero");
    final MockPayload payload1 = sendPayloadData(leaderActor, "one");
    verifyApplyJournalEntries(leaderCollectorActor, 1);
    verifyApplyJournalEntries(follower1CollectorActor, 1);
    verifyApplyJournalEntries(follower2CollectorActor, 1);
    isolateLeader();
    // Send a payload to the isolated leader so it has an uncommitted log entry with index 2.
    testLog.info("Sending payload to isolated leader");
    final MockPayload isolatedLeaderPayload2 = sendPayloadData(leaderActor, "two");
    // Wait for the isolated leader to send AppendEntries to follower1 with the entry at index 2. Note the message
    // is collected but not forwarded to the follower RaftActor.
    AppendEntries appendEntries = expectFirstMatching(follower1CollectorActor, AppendEntries.class);
    assertEquals("getTerm", currentTerm, appendEntries.getTerm());
    assertEquals("getLeaderId", leaderId, appendEntries.getLeaderId());
    assertEquals("getEntries().size()", 1, appendEntries.getEntries().size());
    verifyReplicatedLogEntry(appendEntries.getEntries().get(0), currentTerm, 2, isolatedLeaderPayload2);
    // The leader should transition to IsolatedLeader.
    expectFirstMatching(leaderNotifierActor, RoleChanged.class, rc -> rc.getNewRole().equals(RaftState.IsolatedLeader.name()));
    forceElectionOnFollower1();
    // Send a payload to the new leader follower1 with index 2 and verify it's replicated to follower2
    // and committed.
    testLog.info("Sending payload to new leader");
    final MockPayload newLeaderPayload2 = sendPayloadData(follower1Actor, "two-new");
    verifyApplyJournalEntries(follower1CollectorActor, 2);
    verifyApplyJournalEntries(follower2CollectorActor, 2);
    assertEquals("Follower 1 journal last term", currentTerm, follower1Context.getReplicatedLog().lastTerm());
    assertEquals("Follower 1 journal last index", 2, follower1Context.getReplicatedLog().lastIndex());
    assertEquals("Follower 1 commit index", 2, follower1Context.getCommitIndex());
    verifyReplicatedLogEntry(follower1Context.getReplicatedLog().get(2), currentTerm, 2, newLeaderPayload2);
    assertEquals("Follower 1 state", Lists.newArrayList(payload0, payload1, newLeaderPayload2), 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 a conflicting log entry at index 2 with a different term which should get
    // replaced by the new leader's index 1 entry.
    verifyApplyJournalEntries(leaderCollectorActor, 2);
    assertEquals("Prior leader journal last term", currentTerm, leaderContext.getReplicatedLog().lastTerm());
    assertEquals("Prior leader journal last index", 2, leaderContext.getReplicatedLog().lastIndex());
    assertEquals("Prior leader commit index", 2, leaderContext.getCommitIndex());
    verifyReplicatedLogEntry(leaderContext.getReplicatedLog().get(2), currentTerm, 2, newLeaderPayload2);
    assertEquals("Prior leader state", Lists.newArrayList(payload0, payload1, newLeaderPayload2), leaderActor.underlyingActor().getState());
    testLog.info("testLeaderIsolationWithAllPriorEntriesCommitted ending");
}
Also used : AppendEntries(org.opendaylight.controller.cluster.raft.messages.AppendEntries) MockPayload(org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockPayload) Test(org.junit.Test)

Example 62 with AppendEntries

use of org.opendaylight.controller.cluster.raft.messages.AppendEntries in project controller by opendaylight.

the class AbstractLeader method sendAppendEntriesToFollower.

private void sendAppendEntriesToFollower(final ActorSelection followerActor, final List<ReplicatedLogEntry> entries, final FollowerLogInformation followerLogInformation) {
    // In certain cases outlined below we don't want to send the actual commit index to prevent the follower from
    // possibly committing and applying conflicting entries (those with same index, different term) from a prior
    // term that weren't replicated to a majority, which would be a violation of raft.
    // - if the follower isn't active. In this case we don't know the state of the follower and we send an
    // empty AppendEntries as a heart beat to prevent election.
    // - if we're in the process of installing a snapshot. In this case we don't send any new entries but still
    // need to send AppendEntries to prevent election.
    // - if we're in the process of slicing an AppendEntries with a large log entry payload. In this case we
    // need to send an empty AppendEntries to prevent election.
    boolean isInstallingSnaphot = followerLogInformation.getInstallSnapshotState() != null;
    long leaderCommitIndex = isInstallingSnaphot || followerLogInformation.isLogEntrySlicingInProgress() || !followerLogInformation.isFollowerActive() ? -1 : context.getCommitIndex();
    long followerNextIndex = followerLogInformation.getNextIndex();
    AppendEntries appendEntries = new AppendEntries(currentTerm(), context.getId(), getLogEntryIndex(followerNextIndex - 1), getLogEntryTerm(followerNextIndex - 1), entries, leaderCommitIndex, super.getReplicatedToAllIndex(), context.getPayloadVersion());
    if (!entries.isEmpty() || log.isTraceEnabled()) {
        log.debug("{}: Sending AppendEntries to follower {}: {}", logName(), followerLogInformation.getId(), appendEntries);
    }
    followerActor.tell(appendEntries, actor());
}
Also used : AppendEntries(org.opendaylight.controller.cluster.raft.messages.AppendEntries)

Aggregations

AppendEntries (org.opendaylight.controller.cluster.raft.messages.AppendEntries)62 Test (org.junit.Test)58 MockRaftActorContext (org.opendaylight.controller.cluster.raft.MockRaftActorContext)44 AppendEntriesReply (org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply)34 SimpleReplicatedLogEntry (org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry)34 ReplicatedLogEntry (org.opendaylight.controller.cluster.raft.ReplicatedLogEntry)30 FiniteDuration (scala.concurrent.duration.FiniteDuration)19 DefaultConfigParamsImpl (org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl)17 ByteString (com.google.protobuf.ByteString)15 ActorRef (akka.actor.ActorRef)9 FollowerInitialSyncUpStatus (org.opendaylight.controller.cluster.raft.base.messages.FollowerInitialSyncUpStatus)9 InstallSnapshot (org.opendaylight.controller.cluster.raft.messages.InstallSnapshot)9 TestActorRef (akka.testkit.TestActorRef)8 ArrayList (java.util.ArrayList)8 HashMap (java.util.HashMap)5 MockPayload (org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockPayload)5 FollowerLogInformation (org.opendaylight.controller.cluster.raft.FollowerLogInformation)4 ApplySnapshot (org.opendaylight.controller.cluster.raft.base.messages.ApplySnapshot)4 ApplyState (org.opendaylight.controller.cluster.raft.base.messages.ApplyState)4 Snapshot (org.opendaylight.controller.cluster.raft.persisted.Snapshot)4