Search in sources :

Example 1 with ReplicatedLogEntry

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

the class AbstractLeader method getEntriesToSend.

private List<ReplicatedLogEntry> getEntriesToSend(final FollowerLogInformation followerLogInfo, final ActorSelection followerActor) {
    // Try to get all the entries in the journal but not exceeding the max data size for a single AppendEntries
    // message.
    int maxEntries = (int) context.getReplicatedLog().size();
    final int maxDataSize = context.getConfigParams().getSnapshotChunkSize();
    final long followerNextIndex = followerLogInfo.getNextIndex();
    List<ReplicatedLogEntry> entries = context.getReplicatedLog().getFrom(followerNextIndex, maxEntries, maxDataSize);
    // that is the case, then we need to slice it into smaller chunks.
    if (!(entries.size() == 1 && entries.get(0).getData().size() > maxDataSize)) {
        // Don't need to slice.
        return entries;
    }
    log.debug("{}: Log entry size {} exceeds max payload size {}", logName(), entries.get(0).getData().size(), maxDataSize);
    // If an AppendEntries has already been serialized for the log index then reuse the
    // SharedFileBackedOutputStream.
    final Long logIndex = entries.get(0).getIndex();
    SharedFileBackedOutputStream fileBackedStream = sharedSerializedAppendEntriesStreams.get(logIndex);
    if (fileBackedStream == null) {
        fileBackedStream = context.getFileBackedOutputStreamFactory().newSharedInstance();
        final AppendEntries appendEntries = new AppendEntries(currentTerm(), context.getId(), getLogEntryIndex(followerNextIndex - 1), getLogEntryTerm(followerNextIndex - 1), entries, context.getCommitIndex(), getReplicatedToAllIndex(), context.getPayloadVersion());
        log.debug("{}: Serializing {} for slicing for follower {}", logName(), appendEntries, followerLogInfo.getId());
        try (ObjectOutputStream out = new ObjectOutputStream(fileBackedStream)) {
            out.writeObject(appendEntries);
        } catch (IOException e) {
            log.error("{}: Error serializing {}", logName(), appendEntries, e);
            fileBackedStream.cleanup();
            return Collections.emptyList();
        }
        sharedSerializedAppendEntriesStreams.put(logIndex, fileBackedStream);
        fileBackedStream.setOnCleanupCallback(index -> {
            log.debug("{}: On SharedFileBackedOutputStream cleanup for index {}", logName(), index);
            sharedSerializedAppendEntriesStreams.remove(index);
        }, logIndex);
    } else {
        log.debug("{}: Reusing SharedFileBackedOutputStream for follower {}", logName(), followerLogInfo.getId());
        fileBackedStream.incrementUsageCount();
    }
    log.debug("{}: Slicing stream for index {}, follower {}", logName(), logIndex, followerLogInfo.getId());
    // Record that slicing is in progress for the follower.
    followerLogInfo.setSlicedLogEntryIndex(logIndex);
    final FollowerIdentifier identifier = new FollowerIdentifier(followerLogInfo.getId());
    appendEntriesMessageSlicer.slice(SliceOptions.builder().identifier(identifier).fileBackedOutputStream(fileBackedStream).sendTo(followerActor).replyTo(actor()).onFailureCallback(failure -> {
        log.error("{}: Error slicing AppendEntries for follower {}", logName(), followerLogInfo.getId(), failure);
        followerLogInfo.setSlicedLogEntryIndex(FollowerLogInformation.NO_INDEX);
    }).build());
    return Collections.emptyList();
}
Also used : ReplicatedLogEntry(org.opendaylight.controller.cluster.raft.ReplicatedLogEntry) AppendEntries(org.opendaylight.controller.cluster.raft.messages.AppendEntries) IOException(java.io.IOException) ObjectOutputStream(java.io.ObjectOutputStream) SharedFileBackedOutputStream(org.opendaylight.controller.cluster.io.SharedFileBackedOutputStream)

Example 2 with ReplicatedLogEntry

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

the class AbstractLeader method sendUpdatesToFollower.

/**
 * This method checks if any update needs to be sent to the given follower. This includes append log entries,
 * sending next snapshot chunk, and initiating a snapshot.
 */
private void sendUpdatesToFollower(final String followerId, final FollowerLogInformation followerLogInformation, final boolean sendHeartbeat, final boolean isHeartbeat) {
    ActorSelection followerActor = context.getPeerActorSelection(followerId);
    if (followerActor != null) {
        long followerNextIndex = followerLogInformation.getNextIndex();
        boolean isFollowerActive = followerLogInformation.isFollowerActive();
        boolean sendAppendEntries = false;
        List<ReplicatedLogEntry> entries = Collections.emptyList();
        LeaderInstallSnapshotState installSnapshotState = followerLogInformation.getInstallSnapshotState();
        if (installSnapshotState != null) {
            // if install snapshot is in process , then sent next chunk if possible
            if (isFollowerActive && installSnapshotState.canSendNextChunk()) {
                sendSnapshotChunk(followerActor, followerLogInformation);
            } else if (sendHeartbeat) {
                // we send a heartbeat even if we have not received a reply for the last chunk
                sendAppendEntries = true;
            }
        } else if (followerLogInformation.isLogEntrySlicingInProgress()) {
            sendAppendEntries = sendHeartbeat;
        } else {
            long leaderLastIndex = context.getReplicatedLog().lastIndex();
            long leaderSnapShotIndex = context.getReplicatedLog().getSnapshotIndex();
            if (!isHeartbeat && log.isDebugEnabled() || log.isTraceEnabled()) {
                log.debug("{}: Checking sendAppendEntries for follower {}: active: {}, followerNextIndex: {}, " + "leaderLastIndex: {}, leaderSnapShotIndex: {}", logName(), followerId, isFollowerActive, followerNextIndex, leaderLastIndex, leaderSnapShotIndex);
            }
            if (isFollowerActive && context.getReplicatedLog().isPresent(followerNextIndex)) {
                log.debug("{}: sendAppendEntries: {} is present for follower {}", logName(), followerNextIndex, followerId);
                if (followerLogInformation.okToReplicate()) {
                    entries = getEntriesToSend(followerLogInformation, followerActor);
                    sendAppendEntries = true;
                }
            } else if (isFollowerActive && followerNextIndex >= 0 && leaderLastIndex > followerNextIndex && !context.getSnapshotManager().isCapturing()) {
                // if the followers next index is not present in the leaders log, and
                // if the follower is just not starting and if leader's index is more than followers index
                // then snapshot should be sent
                // Send heartbeat to follower whenever install snapshot is initiated.
                sendAppendEntries = true;
                if (canInstallSnapshot(followerNextIndex)) {
                    log.info("{}: Initiating install snapshot to follower {}: follower nextIndex: {}, leader " + "snapshotIndex: {}, leader lastIndex: {}, leader log size: {}", logName(), followerId, followerNextIndex, leaderSnapShotIndex, leaderLastIndex, context.getReplicatedLog().size());
                    initiateCaptureSnapshot(followerId);
                } else {
                    // It doesn't seem like we should ever reach here - most likely indicates sonething is
                    // wrong.
                    log.info("{}: Follower {} is behind but cannot install snapshot: follower nextIndex: {}, " + "leader snapshotIndex: {}, leader lastIndex: {}, leader log size: {}", logName(), followerId, followerNextIndex, leaderSnapShotIndex, leaderLastIndex, context.getReplicatedLog().size());
                }
            } else if (sendHeartbeat) {
                // we send an AppendEntries, even if the follower is inactive
                // in-order to update the followers timestamp, in case it becomes active again
                sendAppendEntries = true;
            }
        }
        if (sendAppendEntries) {
            sendAppendEntriesToFollower(followerActor, entries, followerLogInformation);
        }
    }
}
Also used : ReplicatedLogEntry(org.opendaylight.controller.cluster.raft.ReplicatedLogEntry) ActorSelection(akka.actor.ActorSelection)

Example 3 with ReplicatedLogEntry

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

the class AbstractShardTest method setupInMemorySnapshotStore.

DataTree setupInMemorySnapshotStore() throws DataValidationFailedException {
    final DataTree testStore = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_OPERATIONAL, SCHEMA_CONTEXT);
    writeToStore(testStore, TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
    final NormalizedNode<?, ?> root = readStore(testStore, YangInstanceIdentifier.EMPTY);
    InMemorySnapshotStore.addSnapshot(shardID.toString(), Snapshot.create(new ShardSnapshotState(new MetadataShardDataTreeSnapshot(root)), Collections.<ReplicatedLogEntry>emptyList(), 0, 1, -1, -1, 1, null, null));
    return testStore;
}
Also used : ReplicatedLogEntry(org.opendaylight.controller.cluster.raft.ReplicatedLogEntry) DataTree(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree) ShardSnapshotState(org.opendaylight.controller.cluster.datastore.persisted.ShardSnapshotState) MetadataShardDataTreeSnapshot(org.opendaylight.controller.cluster.datastore.persisted.MetadataShardDataTreeSnapshot) InMemoryDataTreeFactory(org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory)

Example 4 with ReplicatedLogEntry

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

the class FollowerTest method testHandleSyncUpAppendEntries.

@Test
public void testHandleSyncUpAppendEntries() {
    logStart("testHandleSyncUpAppendEntries");
    MockRaftActorContext context = createActorContext();
    List<ReplicatedLogEntry> entries = Arrays.asList(newReplicatedLogEntry(2, 101, "foo"));
    // The new commitIndex is 101
    AppendEntries appendEntries = new AppendEntries(2, "leader-1", 100, 1, entries, 101, 100, (short) 0);
    follower = createBehavior(context);
    follower.handleMessage(leaderActor, appendEntries);
    FollowerInitialSyncUpStatus syncStatus = MessageCollectorActor.expectFirstMatching(followerActor, FollowerInitialSyncUpStatus.class);
    assertFalse(syncStatus.isInitialSyncDone());
    // Clear all the messages
    MessageCollectorActor.clearMessages(followerActor);
    context.setLastApplied(101);
    context.setCommitIndex(101);
    setLastLogEntry(context, 1, 101, new MockRaftActorContext.MockPayload(""));
    entries = Arrays.asList(newReplicatedLogEntry(2, 101, "foo"));
    // The new commitIndex is 101
    appendEntries = new AppendEntries(2, "leader-1", 101, 1, entries, 102, 101, (short) 0);
    follower.handleMessage(leaderActor, appendEntries);
    syncStatus = MessageCollectorActor.expectFirstMatching(followerActor, FollowerInitialSyncUpStatus.class);
    assertTrue(syncStatus.isInitialSyncDone());
    MessageCollectorActor.clearMessages(followerActor);
    // Sending the same message again should not generate another message
    follower.handleMessage(leaderActor, appendEntries);
    syncStatus = MessageCollectorActor.getFirstMatching(followerActor, FollowerInitialSyncUpStatus.class);
    assertNull(syncStatus);
}
Also used : SimpleReplicatedLogEntry(org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry) ReplicatedLogEntry(org.opendaylight.controller.cluster.raft.ReplicatedLogEntry) 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 5 with ReplicatedLogEntry

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

the class FollowerTest method testHandleFirstAppendEntriesWithPrevIndexMinusOneAndReplicatedToAllIndexPresentInLog.

@Test
public void testHandleFirstAppendEntriesWithPrevIndexMinusOneAndReplicatedToAllIndexPresentInLog() {
    logStart("testHandleFirstAppendEntries");
    MockRaftActorContext context = createActorContext();
    context.getReplicatedLog().clear(0, 2);
    context.getReplicatedLog().append(newReplicatedLogEntry(1, 100, "bar"));
    context.getReplicatedLog().setSnapshotIndex(99);
    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)

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