Search in sources :

Example 21 with AppendEntriesReply

use of org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply 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 22 with AppendEntriesReply

use of org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply 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)

Example 23 with AppendEntriesReply

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

the class RaftActorTest method testFakeSnapshotsForLeaderWithInRealSnapshots.

@Test
public void testFakeSnapshotsForLeaderWithInRealSnapshots() throws Exception {
    final String persistenceId = factory.generateActorId("leader-");
    final String follower1Id = factory.generateActorId("follower-");
    ActorRef followerActor1 = factory.createActor(MessageCollectorActor.props());
    DefaultConfigParamsImpl config = new DefaultConfigParamsImpl();
    config.setHeartBeatInterval(new FiniteDuration(1, TimeUnit.DAYS));
    config.setIsolatedLeaderCheckInterval(new FiniteDuration(1, TimeUnit.DAYS));
    DataPersistenceProvider dataPersistenceProvider = mock(DataPersistenceProvider.class);
    Map<String, String> peerAddresses = new HashMap<>();
    peerAddresses.put(follower1Id, followerActor1.path().toString());
    TestActorRef<MockRaftActor> mockActorRef = factory.createTestActor(MockRaftActor.props(persistenceId, peerAddresses, config, dataPersistenceProvider), persistenceId);
    MockRaftActor leaderActor = mockActorRef.underlyingActor();
    leaderActor.getRaftActorContext().setCommitIndex(4);
    leaderActor.getRaftActorContext().setLastApplied(4);
    leaderActor.getRaftActorContext().getTermInformation().update(1, persistenceId);
    leaderActor.waitForInitializeBehaviorComplete();
    // create 8 entries in the log - 0 to 4 are applied and will get picked up as part of the capture snapshot
    Leader leader = new Leader(leaderActor.getRaftActorContext());
    leaderActor.setCurrentBehavior(leader);
    assertEquals(RaftState.Leader, leaderActor.getCurrentBehavior().state());
    MockRaftActorContext.MockReplicatedLogBuilder logBuilder = new MockRaftActorContext.MockReplicatedLogBuilder();
    leaderActor.getRaftActorContext().setReplicatedLog(logBuilder.createEntries(0, 8, 1).build());
    assertEquals(8, leaderActor.getReplicatedLog().size());
    leaderActor.getRaftActorContext().getSnapshotManager().capture(new SimpleReplicatedLogEntry(6, 1, new MockRaftActorContext.MockPayload("x")), 4);
    verify(leaderActor.snapshotCohortDelegate).createSnapshot(anyObject(), anyObject());
    assertEquals(8, leaderActor.getReplicatedLog().size());
    assertEquals(RaftState.Leader, leaderActor.getCurrentBehavior().state());
    // fake snapshot on index 5
    leaderActor.onReceiveCommand(new AppendEntriesReply(follower1Id, 1, true, 5, 1, (short) 0));
    assertEquals(8, leaderActor.getReplicatedLog().size());
    // fake snapshot on index 6
    assertEquals(RaftState.Leader, leaderActor.getCurrentBehavior().state());
    leaderActor.onReceiveCommand(new AppendEntriesReply(follower1Id, 1, true, 6, 1, (short) 0));
    assertEquals(8, leaderActor.getReplicatedLog().size());
    assertEquals(RaftState.Leader, leaderActor.getCurrentBehavior().state());
    assertEquals(8, leaderActor.getReplicatedLog().size());
    MockSnapshotState snapshotState = new MockSnapshotState(Arrays.asList(new MockRaftActorContext.MockPayload("foo-0"), new MockRaftActorContext.MockPayload("foo-1"), new MockRaftActorContext.MockPayload("foo-2"), new MockRaftActorContext.MockPayload("foo-3"), new MockRaftActorContext.MockPayload("foo-4")));
    leaderActor.getRaftActorContext().getSnapshotManager().persist(snapshotState, java.util.Optional.empty(), Runtime.getRuntime().totalMemory());
    assertTrue(leaderActor.getRaftActorContext().getSnapshotManager().isCapturing());
    // The commit is needed to complete the snapshot creation process
    leaderActor.getRaftActorContext().getSnapshotManager().commit(-1, -1);
    // capture snapshot reply should remove the snapshotted entries only
    assertEquals(3, leaderActor.getReplicatedLog().size());
    assertEquals(7, leaderActor.getReplicatedLog().lastIndex());
    // add another non-replicated entry
    leaderActor.getReplicatedLog().append(new SimpleReplicatedLogEntry(8, 1, new MockRaftActorContext.MockPayload("foo-8")));
    // fake snapshot on index 7, since lastApplied = 7 , we would keep the last applied
    leaderActor.onReceiveCommand(new AppendEntriesReply(follower1Id, 1, true, 7, 1, (short) 0));
    assertEquals(2, leaderActor.getReplicatedLog().size());
    assertEquals(8, leaderActor.getReplicatedLog().lastIndex());
}
Also used : MockSnapshotState(org.opendaylight.controller.cluster.raft.MockRaftActor.MockSnapshotState) Leader(org.opendaylight.controller.cluster.raft.behaviors.Leader) HashMap(java.util.HashMap) ActorRef(akka.actor.ActorRef) TestActorRef(akka.testkit.TestActorRef) FiniteDuration(scala.concurrent.duration.FiniteDuration) ByteString(com.google.protobuf.ByteString) MockPayload(org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockPayload) SimpleReplicatedLogEntry(org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry) AppendEntriesReply(org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply) DataPersistenceProvider(org.opendaylight.controller.cluster.DataPersistenceProvider) Test(org.junit.Test)

Example 24 with AppendEntriesReply

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

the class AbstractRaftActorBehaviorTest method handleAppendEntriesAddSameEntryToLogReply.

protected void handleAppendEntriesAddSameEntryToLogReply(final ActorRef replyActor) {
    AppendEntriesReply reply = MessageCollectorActor.getFirstMatching(replyActor, AppendEntriesReply.class);
    Assert.assertNull("Expected no AppendEntriesReply", reply);
}
Also used : AppendEntriesReply(org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply)

Example 25 with AppendEntriesReply

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

the class FollowerTest method testHandleFirstAppendEntriesWithPrevIndexMinusOne.

@Test
public void testHandleFirstAppendEntriesWithPrevIndexMinusOne() {
    logStart("testHandleFirstAppendEntries");
    MockRaftActorContext context = createActorContext();
    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());
    assertFalse("append entries reply should be false", reply.isSuccess());
}
Also used : SimpleReplicatedLogEntry(org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry) ReplicatedLogEntry(org.opendaylight.controller.cluster.raft.ReplicatedLogEntry) AppendEntriesReply(org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply) MockRaftActorContext(org.opendaylight.controller.cluster.raft.MockRaftActorContext) AppendEntries(org.opendaylight.controller.cluster.raft.messages.AppendEntries) FollowerInitialSyncUpStatus(org.opendaylight.controller.cluster.raft.base.messages.FollowerInitialSyncUpStatus) Test(org.junit.Test)

Aggregations

AppendEntriesReply (org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply)57 Test (org.junit.Test)48 MockRaftActorContext (org.opendaylight.controller.cluster.raft.MockRaftActorContext)40 AppendEntries (org.opendaylight.controller.cluster.raft.messages.AppendEntries)34 FiniteDuration (scala.concurrent.duration.FiniteDuration)23 DefaultConfigParamsImpl (org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl)22 SimpleReplicatedLogEntry (org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry)17 ReplicatedLogEntry (org.opendaylight.controller.cluster.raft.ReplicatedLogEntry)15 ByteString (com.google.protobuf.ByteString)13 HashMap (java.util.HashMap)8 ActorRef (akka.actor.ActorRef)7 TestActorRef (akka.testkit.TestActorRef)6 InstallSnapshot (org.opendaylight.controller.cluster.raft.messages.InstallSnapshot)6 FollowerLogInformation (org.opendaylight.controller.cluster.raft.FollowerLogInformation)5 FollowerInitialSyncUpStatus (org.opendaylight.controller.cluster.raft.base.messages.FollowerInitialSyncUpStatus)5 AtomicReference (java.util.concurrent.atomic.AtomicReference)4 MockPayload (org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockPayload)4 RaftActorLeadershipTransferCohort (org.opendaylight.controller.cluster.raft.RaftActorLeadershipTransferCohort)4 ApplySnapshot (org.opendaylight.controller.cluster.raft.base.messages.ApplySnapshot)4 ApplyState (org.opendaylight.controller.cluster.raft.base.messages.ApplyState)4