Search in sources :

Example 36 with MockRaftActorContext

use of org.opendaylight.controller.cluster.raft.MockRaftActorContext in project controller by opendaylight.

the class LeaderTest method testThatLeaderSendsAHeartbeatMessageToAllFollowers.

@Test
public void testThatLeaderSendsAHeartbeatMessageToAllFollowers() throws Exception {
    logStart("testThatLeaderSendsAHeartbeatMessageToAllFollowers");
    MockRaftActorContext actorContext = createActorContextWithFollower();
    actorContext.setCommitIndex(-1);
    actorContext.setPayloadVersion(payloadVersion);
    long term = 1;
    actorContext.getTermInformation().update(term, "");
    leader = new Leader(actorContext);
    actorContext.setCurrentBehavior(leader);
    // Leader should send an immediate heartbeat with no entries as follower is inactive.
    final long lastIndex = actorContext.getReplicatedLog().lastIndex();
    AppendEntries appendEntries = MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
    assertEquals("getTerm", term, appendEntries.getTerm());
    assertEquals("getPrevLogIndex", -1, appendEntries.getPrevLogIndex());
    assertEquals("getPrevLogTerm", -1, appendEntries.getPrevLogTerm());
    assertEquals("Entries size", 0, appendEntries.getEntries().size());
    assertEquals("getPayloadVersion", payloadVersion, appendEntries.getPayloadVersion());
    // The follower would normally reply - simulate that explicitly here.
    leader.handleMessage(followerActor, new AppendEntriesReply(FOLLOWER_ID, term, true, lastIndex - 1, term, (short) 0));
    assertEquals("isFollowerActive", true, leader.getFollower(FOLLOWER_ID).isFollowerActive());
    followerActor.underlyingActor().clear();
    // Sleep for the heartbeat interval so AppendEntries is sent.
    Uninterruptibles.sleepUninterruptibly(actorContext.getConfigParams().getHeartBeatInterval().toMillis(), TimeUnit.MILLISECONDS);
    leader.handleMessage(leaderActor, SendHeartBeat.INSTANCE);
    appendEntries = MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
    assertEquals("getPrevLogIndex", lastIndex - 1, appendEntries.getPrevLogIndex());
    assertEquals("getPrevLogTerm", term, appendEntries.getPrevLogTerm());
    assertEquals("Entries size", 1, appendEntries.getEntries().size());
    assertEquals("Entry getIndex", lastIndex, appendEntries.getEntries().get(0).getIndex());
    assertEquals("Entry getTerm", term, appendEntries.getEntries().get(0).getTerm());
    assertEquals("getPayloadVersion", payloadVersion, appendEntries.getPayloadVersion());
}
Also used : AppendEntriesReply(org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply) MockRaftActorContext(org.opendaylight.controller.cluster.raft.MockRaftActorContext) AppendEntries(org.opendaylight.controller.cluster.raft.messages.AppendEntries) Test(org.junit.Test)

Example 37 with MockRaftActorContext

use of org.opendaylight.controller.cluster.raft.MockRaftActorContext in project controller by opendaylight.

the class LeaderTest method testHandleReplicateMessageWithHigherTermThanPreviousEntry.

@Test
public void testHandleReplicateMessageWithHigherTermThanPreviousEntry() throws Exception {
    logStart("testHandleReplicateMessageWithHigherTermThanPreviousEntry");
    MockRaftActorContext actorContext = createActorContextWithFollower();
    actorContext.setCommitIndex(-1);
    actorContext.setLastApplied(-1);
    // The raft context is initialized with a couple log entries. However the commitIndex
    // is -1, simulating that the leader previously didn't get consensus and thus the log entries weren't
    // committed and applied. Now it regains leadership with a higher term (2).
    long prevTerm = actorContext.getTermInformation().getCurrentTerm();
    long newTerm = prevTerm + 1;
    actorContext.getTermInformation().update(newTerm, "");
    leader = new Leader(actorContext);
    actorContext.setCurrentBehavior(leader);
    // Leader will send an immediate heartbeat - ignore it.
    MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
    // The follower replies with the leader's current last index and term, simulating that it is
    // up to date with the leader.
    long lastIndex = actorContext.getReplicatedLog().lastIndex();
    leader.handleMessage(followerActor, new AppendEntriesReply(FOLLOWER_ID, newTerm, true, lastIndex, prevTerm, (short) 0));
    // The commit index should not get updated even though consensus was reached. This is b/c the
    // last entry's term does match the current term. As per §5.4.1, "Raft never commits log entries
    // from previous terms by counting replicas".
    assertEquals("Commit Index", -1, actorContext.getCommitIndex());
    followerActor.underlyingActor().clear();
    // Now replicate a new entry with the new term 2.
    long newIndex = lastIndex + 1;
    sendReplicate(actorContext, newTerm, newIndex);
    AppendEntries appendEntries = MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
    assertEquals("getPrevLogIndex", lastIndex, appendEntries.getPrevLogIndex());
    assertEquals("getPrevLogTerm", prevTerm, appendEntries.getPrevLogTerm());
    assertEquals("Entries size", 1, appendEntries.getEntries().size());
    assertEquals("Entry getIndex", newIndex, appendEntries.getEntries().get(0).getIndex());
    assertEquals("Entry getTerm", newTerm, appendEntries.getEntries().get(0).getTerm());
    assertEquals("Entry payload", "foo", appendEntries.getEntries().get(0).getData().toString());
    // The follower replies with success. The leader should now update the commit index to the new index
    // as per §5.4.1 "once an entry from the current term is committed by counting replicas, then all
    // prior entries are committed indirectly".
    leader.handleMessage(followerActor, new AppendEntriesReply(FOLLOWER_ID, newTerm, true, newIndex, newTerm, (short) 0));
    assertEquals("Commit Index", newIndex, actorContext.getCommitIndex());
}
Also used : AppendEntriesReply(org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply) MockRaftActorContext(org.opendaylight.controller.cluster.raft.MockRaftActorContext) AppendEntries(org.opendaylight.controller.cluster.raft.messages.AppendEntries) Test(org.junit.Test)

Example 38 with MockRaftActorContext

use of org.opendaylight.controller.cluster.raft.MockRaftActorContext in project controller by opendaylight.

the class LeaderTest method testHandleReplicateMessageWhenThereAreNoFollowers.

@Test
public void testHandleReplicateMessageWhenThereAreNoFollowers() throws Exception {
    logStart("testHandleReplicateMessageWhenThereAreNoFollowers");
    MockRaftActorContext actorContext = createActorContext();
    leader = new Leader(actorContext);
    actorContext.setLastApplied(0);
    long newLogIndex = actorContext.getReplicatedLog().lastIndex() + 1;
    long term = actorContext.getTermInformation().getCurrentTerm();
    ReplicatedLogEntry newEntry = new SimpleReplicatedLogEntry(newLogIndex, term, new MockRaftActorContext.MockPayload("foo"));
    actorContext.getReplicatedLog().append(newEntry);
    final Identifier id = new MockIdentifier("state-id");
    RaftActorBehavior raftBehavior = leader.handleMessage(leaderActor, new Replicate(leaderActor, id, newEntry, true));
    // State should not change
    assertTrue(raftBehavior instanceof Leader);
    assertEquals("getCommitIndex", newLogIndex, actorContext.getCommitIndex());
    // We should get 2 ApplyState messages - 1 for new log entry and 1 for the previous
    // one since lastApplied state is 0.
    List<ApplyState> applyStateList = MessageCollectorActor.getAllMatching(leaderActor, ApplyState.class);
    assertEquals("ApplyState count", newLogIndex, applyStateList.size());
    for (int i = 0; i <= newLogIndex - 1; i++) {
        ApplyState applyState = applyStateList.get(i);
        assertEquals("getIndex", i + 1, applyState.getReplicatedLogEntry().getIndex());
        assertEquals("getTerm", term, applyState.getReplicatedLogEntry().getTerm());
    }
    ApplyState last = applyStateList.get((int) newLogIndex - 1);
    assertEquals("getData", newEntry.getData(), last.getReplicatedLogEntry().getData());
    assertEquals("getIdentifier", id, last.getIdentifier());
}
Also used : MockRaftActorContext(org.opendaylight.controller.cluster.raft.MockRaftActorContext) SimpleReplicatedLogEntry(org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry) ReplicatedLogEntry(org.opendaylight.controller.cluster.raft.ReplicatedLogEntry) Identifier(org.opendaylight.yangtools.concepts.Identifier) Replicate(org.opendaylight.controller.cluster.raft.base.messages.Replicate) SimpleReplicatedLogEntry(org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry) ApplyState(org.opendaylight.controller.cluster.raft.base.messages.ApplyState) Test(org.junit.Test)

Example 39 with MockRaftActorContext

use of org.opendaylight.controller.cluster.raft.MockRaftActorContext in project controller by opendaylight.

the class LeaderTest method testIsolatedLeaderCheckNoVotingFollowers.

@Test
public void testIsolatedLeaderCheckNoVotingFollowers() {
    logStart("testIsolatedLeaderCheckNoVotingFollowers");
    MockRaftActorContext followerActorContext = createFollowerActorContextWithLeader();
    Follower follower = new Follower(followerActorContext);
    followerActor.underlyingActor().setBehavior(follower);
    MockRaftActorContext leaderActorContext = createActorContextWithFollower();
    ((DefaultConfigParamsImpl) leaderActorContext.getConfigParams()).setHeartBeatInterval(new FiniteDuration(1000, TimeUnit.SECONDS));
    leaderActorContext.getPeerInfo(FOLLOWER_ID).setVotingState(VotingState.NON_VOTING);
    leader = new Leader(leaderActorContext);
    leader.getFollower(FOLLOWER_ID).markFollowerActive();
    RaftActorBehavior newBehavior = leader.handleMessage(leaderActor, Leader.ISOLATED_LEADER_CHECK);
    assertTrue("Expected Leader", newBehavior instanceof Leader);
}
Also used : MockRaftActorContext(org.opendaylight.controller.cluster.raft.MockRaftActorContext) FiniteDuration(scala.concurrent.duration.FiniteDuration) DefaultConfigParamsImpl(org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl) Test(org.junit.Test)

Example 40 with MockRaftActorContext

use of org.opendaylight.controller.cluster.raft.MockRaftActorContext in project controller by opendaylight.

the class LeaderTest method testReplicationConsensusWithNonVotingFollower.

@Test
public void testReplicationConsensusWithNonVotingFollower() {
    logStart("testReplicationConsensusWithNonVotingFollower");
    MockRaftActorContext leaderActorContext = createActorContextWithFollower();
    ((DefaultConfigParamsImpl) leaderActorContext.getConfigParams()).setHeartBeatInterval(new FiniteDuration(1000, TimeUnit.SECONDS));
    leaderActorContext.setReplicatedLog(new MockRaftActorContext.MockReplicatedLogBuilder().build());
    leaderActorContext.setCommitIndex(-1);
    leaderActorContext.setLastApplied(-1);
    String nonVotingFollowerId = "nonvoting-follower";
    ActorRef nonVotingFollowerActor = actorFactory.createActor(MessageCollectorActor.props(), actorFactory.generateActorId(nonVotingFollowerId));
    leaderActorContext.addToPeers(nonVotingFollowerId, nonVotingFollowerActor.path().toString(), VotingState.NON_VOTING);
    leader = new Leader(leaderActorContext);
    leaderActorContext.setCurrentBehavior(leader);
    // Ignore initial heartbeats
    MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
    MessageCollectorActor.expectFirstMatching(nonVotingFollowerActor, AppendEntries.class);
    MessageCollectorActor.clearMessages(followerActor);
    MessageCollectorActor.clearMessages(nonVotingFollowerActor);
    MessageCollectorActor.clearMessages(leaderActor);
    // Send a Replicate message and wait for AppendEntries.
    sendReplicate(leaderActorContext, 0);
    MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
    MessageCollectorActor.expectFirstMatching(nonVotingFollowerActor, AppendEntries.class);
    // Send reply only from the voting follower and verify consensus via ApplyState.
    leader.handleMessage(leaderActor, new AppendEntriesReply(FOLLOWER_ID, 1, true, 0, 1, (short) 0));
    MessageCollectorActor.expectFirstMatching(leaderActor, ApplyState.class);
    leader.handleMessage(leaderActor, new AppendEntriesReply(nonVotingFollowerId, 1, true, 0, 1, (short) 0));
    MessageCollectorActor.clearMessages(followerActor);
    MessageCollectorActor.clearMessages(nonVotingFollowerActor);
    MessageCollectorActor.clearMessages(leaderActor);
    // Send another Replicate message
    sendReplicate(leaderActorContext, 1);
    MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
    AppendEntries appendEntries = MessageCollectorActor.expectFirstMatching(nonVotingFollowerActor, AppendEntries.class);
    assertEquals("Log entries size", 1, appendEntries.getEntries().size());
    assertEquals("Log entry index", 1, appendEntries.getEntries().get(0).getIndex());
    // Send reply only from the non-voting follower and verify no consensus via no ApplyState.
    leader.handleMessage(leaderActor, new AppendEntriesReply(nonVotingFollowerId, 1, true, 1, 1, (short) 0));
    MessageCollectorActor.assertNoneMatching(leaderActor, ApplyState.class, 500);
    // Send reply from the voting follower and verify consensus.
    leader.handleMessage(leaderActor, new AppendEntriesReply(FOLLOWER_ID, 1, true, 1, 1, (short) 0));
    MessageCollectorActor.expectFirstMatching(leaderActor, ApplyState.class);
}
Also used : AppendEntriesReply(org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply) ActorRef(akka.actor.ActorRef) TestActorRef(akka.testkit.TestActorRef) MockRaftActorContext(org.opendaylight.controller.cluster.raft.MockRaftActorContext) FiniteDuration(scala.concurrent.duration.FiniteDuration) DefaultConfigParamsImpl(org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl) ByteString(com.google.protobuf.ByteString) AppendEntries(org.opendaylight.controller.cluster.raft.messages.AppendEntries) Test(org.junit.Test)

Aggregations

MockRaftActorContext (org.opendaylight.controller.cluster.raft.MockRaftActorContext)100 Test (org.junit.Test)93 AppendEntries (org.opendaylight.controller.cluster.raft.messages.AppendEntries)44 AppendEntriesReply (org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply)40 DefaultConfigParamsImpl (org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl)30 FiniteDuration (scala.concurrent.duration.FiniteDuration)29 SimpleReplicatedLogEntry (org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry)28 ReplicatedLogEntry (org.opendaylight.controller.cluster.raft.ReplicatedLogEntry)24 ByteString (com.google.protobuf.ByteString)19 HashMap (java.util.HashMap)16 InstallSnapshot (org.opendaylight.controller.cluster.raft.messages.InstallSnapshot)11 RequestVoteReply (org.opendaylight.controller.cluster.raft.messages.RequestVoteReply)11 FollowerInitialSyncUpStatus (org.opendaylight.controller.cluster.raft.base.messages.FollowerInitialSyncUpStatus)9 RequestVote (org.opendaylight.controller.cluster.raft.messages.RequestVote)8 ArrayList (java.util.ArrayList)7 AbstractActorTest (org.opendaylight.controller.cluster.raft.AbstractActorTest)7 CaptureSnapshot (org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot)7 FollowerLogInformation (org.opendaylight.controller.cluster.raft.FollowerLogInformation)6 SendInstallSnapshot (org.opendaylight.controller.cluster.raft.base.messages.SendInstallSnapshot)6 InstallSnapshotReply (org.opendaylight.controller.cluster.raft.messages.InstallSnapshotReply)6