Search in sources :

Example 31 with ReplicatedLogEntry

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

the class FollowerTest method testHandleAppendEntriesPreviousLogEntryMissing.

@Test
public void testHandleAppendEntriesPreviousLogEntryMissing() {
    logStart("testHandleAppendEntriesPreviousLogEntryMissing");
    final MockRaftActorContext context = createActorContext();
    // Prepare the receivers log
    MockRaftActorContext.SimpleReplicatedLog log = new MockRaftActorContext.SimpleReplicatedLog();
    log.append(newReplicatedLogEntry(1, 0, "zero"));
    log.append(newReplicatedLogEntry(1, 1, "one"));
    log.append(newReplicatedLogEntry(1, 2, "two"));
    context.setReplicatedLog(log);
    // Prepare the entries to be sent with AppendEntries
    List<ReplicatedLogEntry> entries = new ArrayList<>();
    entries.add(newReplicatedLogEntry(1, 4, "four"));
    AppendEntries appendEntries = new AppendEntries(1, "leader", 3, 1, entries, 4, -1, (short) 0);
    follower = createBehavior(context);
    RaftActorBehavior newBehavior = follower.handleMessage(leaderActor, appendEntries);
    Assert.assertSame(follower, newBehavior);
    expectAndVerifyAppendEntriesReply(1, false, context.getId(), 1, 2);
}
Also used : SimpleReplicatedLogEntry(org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry) ReplicatedLogEntry(org.opendaylight.controller.cluster.raft.ReplicatedLogEntry) MockRaftActorContext(org.opendaylight.controller.cluster.raft.MockRaftActorContext) ArrayList(java.util.ArrayList) AppendEntries(org.opendaylight.controller.cluster.raft.messages.AppendEntries) Test(org.junit.Test)

Example 32 with ReplicatedLogEntry

use of org.opendaylight.controller.cluster.raft.ReplicatedLogEntry 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 33 with ReplicatedLogEntry

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

the class AbstractRaftActorBehavior method applyLogToStateMachine.

/**
 * Applies the log entries up to the specified index that is known to be committed to the state machine.
 *
 * @param index the log index
 */
protected void applyLogToStateMachine(final long index) {
    // Now maybe we apply to the state machine
    for (long i = context.getLastApplied() + 1; i < index + 1; i++) {
        ReplicatedLogEntry replicatedLogEntry = context.getReplicatedLog().get(i);
        if (replicatedLogEntry != null) {
            // Send a local message to the local RaftActor (it's derived class to be
            // specific to apply the log to it's index)
            final ApplyState applyState;
            final ClientRequestTracker tracker = removeClientRequestTracker(i);
            if (tracker != null) {
                applyState = new ApplyState(tracker.getClientActor(), tracker.getIdentifier(), replicatedLogEntry);
            } else {
                applyState = new ApplyState(null, null, replicatedLogEntry);
            }
            log.debug("{}: Setting last applied to {}", logName(), i);
            context.setLastApplied(i);
            context.getApplyStateConsumer().accept(applyState);
        } else {
            // if one index is not present in the log, no point in looping
            // around as the rest wont be present either
            log.warn("{}: Missing index {} from log. Cannot apply state. Ignoring {} to {}", logName(), i, i, index);
            break;
        }
    }
    // send a message to persist a ApplyLogEntries marker message into akka's persistent journal
    // will be used during recovery
    // in case if the above code throws an error and this message is not sent, it would be fine
    // as the  append entries received later would initiate add this message to the journal
    actor().tell(new ApplyJournalEntries(context.getLastApplied()), actor());
}
Also used : ClientRequestTracker(org.opendaylight.controller.cluster.raft.ClientRequestTracker) ReplicatedLogEntry(org.opendaylight.controller.cluster.raft.ReplicatedLogEntry) ApplyJournalEntries(org.opendaylight.controller.cluster.raft.persisted.ApplyJournalEntries) ApplyState(org.opendaylight.controller.cluster.raft.base.messages.ApplyState)

Example 34 with ReplicatedLogEntry

use of org.opendaylight.controller.cluster.raft.ReplicatedLogEntry 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 35 with ReplicatedLogEntry

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

the class AppendEntriesTest method testSerialization.

@Test
public void testSerialization() {
    ReplicatedLogEntry entry1 = new SimpleReplicatedLogEntry(1, 2, new MockPayload("payload1"));
    ReplicatedLogEntry entry2 = new SimpleReplicatedLogEntry(3, 4, new MockPayload("payload2"));
    short payloadVersion = 5;
    AppendEntries expected = new AppendEntries(5L, "node1", 7L, 8L, Arrays.asList(entry1, entry2), 10L, -1, payloadVersion);
    AppendEntries cloned = (AppendEntries) SerializationUtils.clone(expected);
    verifyAppendEntries(expected, cloned);
}
Also used : SimpleReplicatedLogEntry(org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry) ReplicatedLogEntry(org.opendaylight.controller.cluster.raft.ReplicatedLogEntry) SimpleReplicatedLogEntry(org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry) MockPayload(org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockPayload) Test(org.junit.Test)

Aggregations

ReplicatedLogEntry (org.opendaylight.controller.cluster.raft.ReplicatedLogEntry)38 SimpleReplicatedLogEntry (org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry)31 Test (org.junit.Test)30 AppendEntries (org.opendaylight.controller.cluster.raft.messages.AppendEntries)30 MockRaftActorContext (org.opendaylight.controller.cluster.raft.MockRaftActorContext)24 AppendEntriesReply (org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply)15 FollowerInitialSyncUpStatus (org.opendaylight.controller.cluster.raft.base.messages.FollowerInitialSyncUpStatus)9 ArrayList (java.util.ArrayList)8 DefaultConfigParamsImpl (org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl)7 ByteString (com.google.protobuf.ByteString)5 FollowerLogInformation (org.opendaylight.controller.cluster.raft.FollowerLogInformation)5 ApplyState (org.opendaylight.controller.cluster.raft.base.messages.ApplyState)5 InstallSnapshot (org.opendaylight.controller.cluster.raft.messages.InstallSnapshot)5 ApplySnapshot (org.opendaylight.controller.cluster.raft.base.messages.ApplySnapshot)4 Snapshot (org.opendaylight.controller.cluster.raft.persisted.Snapshot)4 FiniteDuration (scala.concurrent.duration.FiniteDuration)4 AtomicReference (java.util.concurrent.atomic.AtomicReference)3 MockRaftActor (org.opendaylight.controller.cluster.raft.MockRaftActor)3 Builder (org.opendaylight.controller.cluster.raft.MockRaftActor.Builder)3 ActorSelection (akka.actor.ActorSelection)2