Search in sources :

Example 51 with AppendEntriesReply

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

the class FollowerTest method testHandleAppendEntriesSenderPrevLogTermNotSameAsReceiverPrevLogTerm.

/**
 * This test verifies that when an AppendEntries is received a specific prevLogTerm
 * which does not match the term that is in RaftActors log entry at prevLogIndex
 * then the RaftActor does not change it's state and it returns a failure.
 */
@Test
public void testHandleAppendEntriesSenderPrevLogTermNotSameAsReceiverPrevLogTerm() {
    logStart("testHandleAppendEntriesSenderPrevLogTermNotSameAsReceiverPrevLogTerm");
    MockRaftActorContext context = createActorContext();
    // First set the receivers term to lower number
    context.getTermInformation().update(95, "test");
    // AppendEntries is now sent with a bigger term
    // this will set the receivers term to be the same as the sender's term
    AppendEntries appendEntries = new AppendEntries(100, "leader", 0, 0, Collections.emptyList(), 101, -1, (short) 0);
    follower = createBehavior(context);
    RaftActorBehavior newBehavior = follower.handleMessage(leaderActor, appendEntries);
    Assert.assertSame(follower, newBehavior);
    AppendEntriesReply reply = MessageCollectorActor.expectFirstMatching(leaderActor, AppendEntriesReply.class);
    assertEquals("isSuccess", false, reply.isSuccess());
}
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 52 with AppendEntriesReply

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

the class FollowerTest method testReceivingAppendEntriesDuringInstallSnapshotFromDifferentLeader.

@Test
public void testReceivingAppendEntriesDuringInstallSnapshotFromDifferentLeader() {
    logStart("testReceivingAppendEntriesDuringInstallSnapshotFromDifferentLeader");
    MockRaftActorContext context = createActorContext();
    follower = createBehavior(context);
    ByteString bsSnapshot = createSnapshot();
    int snapshotLength = bsSnapshot.size();
    int chunkSize = 50;
    int totalChunks = snapshotLength / chunkSize + (snapshotLength % chunkSize > 0 ? 1 : 0);
    int lastIncludedIndex = 1;
    // Check that snapshot installation is not in progress
    assertNull(follower.getSnapshotTracker());
    // Make sure that we have more than 1 chunk to send
    assertTrue(totalChunks > 1);
    // Send an install snapshot with the first chunk to start the process of installing a snapshot
    byte[] chunkData = getNextChunk(bsSnapshot, 0, chunkSize);
    follower.handleMessage(leaderActor, new InstallSnapshot(1, "leader", lastIncludedIndex, 1, chunkData, 1, totalChunks));
    // Check if snapshot installation is in progress now
    assertNotNull(follower.getSnapshotTracker());
    // Send appendEntries with a new term and leader.
    AppendEntries appendEntries = new AppendEntries(2, "new-leader", 1, 1, Arrays.asList(newReplicatedLogEntry(2, 2, "3")), 2, -1, (short) 1);
    follower.handleMessage(leaderActor, appendEntries);
    AppendEntriesReply reply = MessageCollectorActor.expectFirstMatching(leaderActor, AppendEntriesReply.class);
    assertEquals("isSuccess", true, reply.isSuccess());
    assertEquals("getLogLastIndex", 2, reply.getLogLastIndex());
    assertEquals("getLogLastTerm", 2, reply.getLogLastTerm());
    assertEquals("getTerm", 2, reply.getTerm());
    assertNull(follower.getSnapshotTracker());
}
Also used : AppendEntriesReply(org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply) ByteString(com.google.protobuf.ByteString) MockRaftActorContext(org.opendaylight.controller.cluster.raft.MockRaftActorContext) AppendEntries(org.opendaylight.controller.cluster.raft.messages.AppendEntries) InstallSnapshot(org.opendaylight.controller.cluster.raft.messages.InstallSnapshot) Test(org.junit.Test)

Example 53 with AppendEntriesReply

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

the class FollowerTest method testHandleFirstAppendEntriesWithPrevIndexMinusOneAndReplicatedToAllIndexPresentInSnapshot.

@Test
public void testHandleFirstAppendEntriesWithPrevIndexMinusOneAndReplicatedToAllIndexPresentInSnapshot() {
    logStart("testHandleFirstAppendEntries");
    MockRaftActorContext context = createActorContext();
    context.getReplicatedLog().clear(0, 2);
    context.getReplicatedLog().setSnapshotIndex(100);
    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());
    assertTrue("append entries reply should be true", 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)

Example 54 with AppendEntriesReply

use of org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply 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;
}
Also used : Stopwatch(com.google.common.base.Stopwatch) RaftActorContext(org.opendaylight.controller.cluster.raft.RaftActorContext) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) TimeoutNow(org.opendaylight.controller.cluster.raft.base.messages.TimeoutNow) ActorSelection(akka.actor.ActorSelection) ElectionTimeout(org.opendaylight.controller.cluster.raft.base.messages.ElectionTimeout) ArrayList(java.util.ArrayList) RaftRPC(org.opendaylight.controller.cluster.raft.messages.RaftRPC) ApplySnapshot(org.opendaylight.controller.cluster.raft.base.messages.ApplySnapshot) ActorRef(akka.actor.ActorRef) Cluster(akka.cluster.Cluster) Member(akka.cluster.Member) MemberStatus(akka.cluster.MemberStatus) Nullable(javax.annotation.Nullable) Address(akka.actor.Address) AppendEntries(org.opendaylight.controller.cluster.raft.messages.AppendEntries) ServerConfigurationPayload(org.opendaylight.controller.cluster.raft.persisted.ServerConfigurationPayload) ReplicatedLogEntry(org.opendaylight.controller.cluster.raft.ReplicatedLogEntry) Set(java.util.Set) RaftState(org.opendaylight.controller.cluster.raft.RaftState) IOException(java.io.IOException) RequestVote(org.opendaylight.controller.cluster.raft.messages.RequestVote) RequestVoteReply(org.opendaylight.controller.cluster.raft.messages.RequestVoteReply) TimeUnit(java.util.concurrent.TimeUnit) List(java.util.List) Snapshot(org.opendaylight.controller.cluster.raft.persisted.Snapshot) InstallSnapshotReply(org.opendaylight.controller.cluster.raft.messages.InstallSnapshotReply) CurrentClusterState(akka.cluster.ClusterEvent.CurrentClusterState) Optional(java.util.Optional) MessageAssembler(org.opendaylight.controller.cluster.messaging.MessageAssembler) Procedure(akka.japi.Procedure) VisibleForTesting(com.google.common.annotations.VisibleForTesting) AppendEntriesReply(org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply) InstallSnapshot(org.opendaylight.controller.cluster.raft.messages.InstallSnapshot) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) ReplicatedLogEntry(org.opendaylight.controller.cluster.raft.ReplicatedLogEntry) ServerConfigurationPayload(org.opendaylight.controller.cluster.raft.persisted.ServerConfigurationPayload) AppendEntriesReply(org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply) ArrayList(java.util.ArrayList) List(java.util.List)

Example 55 with AppendEntriesReply

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

the class Follower method sendOutOfSyncAppendEntriesReply.

private void sendOutOfSyncAppendEntriesReply(final ActorRef sender, boolean forceInstallSnapshot) {
    // We found that the log was out of sync so just send a negative reply.
    final AppendEntriesReply reply = new AppendEntriesReply(context.getId(), currentTerm(), false, lastIndex(), lastTerm(), context.getPayloadVersion(), forceInstallSnapshot);
    log.info("{}: Follower is out-of-sync so sending negative reply: {}", logName(), reply);
    sender.tell(reply, actor());
}
Also used : AppendEntriesReply(org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply)

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