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();
}
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);
}
}
}
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;
}
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);
}
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());
}
Aggregations