package org.opendaylight.controller.cluster.raft;
import akka.japi.Procedure;
+import com.google.common.base.Preconditions;
import java.util.Collections;
import java.util.List;
-import org.opendaylight.controller.cluster.DataPersistenceProvider;
-import org.opendaylight.controller.cluster.raft.RaftActor.DeleteEntries;
-import org.opendaylight.controller.cluster.raft.behaviors.RaftActorBehavior;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.controller.cluster.raft.persisted.DeleteEntries;
+import org.opendaylight.controller.cluster.raft.persisted.Snapshot;
/**
* Implementation of ReplicatedLog used by the RaftActor.
class ReplicatedLogImpl extends AbstractReplicatedLogImpl {
private static final int DATA_SIZE_DIVIDER = 5;
- private long dataSizeSinceLastSnapshot = 0L;
private final RaftActorContext context;
- private final DataPersistenceProvider persistence;
- private final RaftActorBehavior currentBehavior;
-
- private final Procedure<DeleteEntries> deleteProcedure = new Procedure<DeleteEntries>() {
- @Override
- public void apply(DeleteEntries param) {
- dataSize = 0;
- for (ReplicatedLogEntry entry : journal) {
- dataSize += entry.size();
- }
- }
- };
+ private long dataSizeSinceLastSnapshot = 0L;
- static ReplicatedLog newInstance(Snapshot snapshot, RaftActorContext context,
- DataPersistenceProvider persistence, RaftActorBehavior currentBehavior) {
- return new ReplicatedLogImpl(snapshot.getLastAppliedIndex(), snapshot.getLastAppliedTerm(),
- snapshot.getUnAppliedEntries(), context, persistence, currentBehavior);
+ private ReplicatedLogImpl(final long snapshotIndex, final long snapshotTerm,
+ final List<ReplicatedLogEntry> unAppliedEntries,
+ final RaftActorContext context) {
+ super(snapshotIndex, snapshotTerm, unAppliedEntries, context.getId());
+ this.context = Preconditions.checkNotNull(context);
}
- static ReplicatedLog newInstance(RaftActorContext context,
- DataPersistenceProvider persistence, RaftActorBehavior currentBehavior) {
- return new ReplicatedLogImpl(-1L, -1L, Collections.<ReplicatedLogEntry>emptyList(), context,
- persistence, currentBehavior);
+ static ReplicatedLog newInstance(final Snapshot snapshot, final RaftActorContext context) {
+ return new ReplicatedLogImpl(snapshot.getLastAppliedIndex(), snapshot.getLastAppliedTerm(),
+ snapshot.getUnAppliedEntries(), context);
}
- private ReplicatedLogImpl(long snapshotIndex, long snapshotTerm, List<ReplicatedLogEntry> unAppliedEntries,
- RaftActorContext context, DataPersistenceProvider persistence, RaftActorBehavior currentBehavior) {
- super(snapshotIndex, snapshotTerm, unAppliedEntries);
- this.context = context;
- this.persistence = persistence;
- this.currentBehavior = currentBehavior;
+ static ReplicatedLog newInstance(final RaftActorContext context) {
+ return new ReplicatedLogImpl(-1L, -1L, Collections.<ReplicatedLogEntry>emptyList(), context);
}
@Override
- public void removeFromAndPersist(long logEntryIndex) {
- int adjustedIndex = adjustedIndex(logEntryIndex);
-
- if (adjustedIndex < 0) {
- return;
+ public boolean removeFromAndPersist(final long logEntryIndex) {
+ // FIXME: Maybe this should be done after the command is saved
+ long adjustedIndex = removeFrom(logEntryIndex);
+ if (adjustedIndex >= 0) {
+ context.getPersistenceProvider().persist(new DeleteEntries(adjustedIndex), NoopProcedure.instance());
+ return true;
}
- // FIXME: Maybe this should be done after the command is saved
- journal.subList(adjustedIndex , journal.size()).clear();
+ return false;
+ }
+
+ @Override
+ public boolean shouldCaptureSnapshot(long logIndex) {
+ final ConfigParams config = context.getConfigParams();
+ final long journalSize = logIndex + 1;
+ final long dataThreshold = context.getTotalMemory() * config.getSnapshotDataThresholdPercentage() / 100;
- persistence.persist(new DeleteEntries(adjustedIndex), deleteProcedure);
+ return journalSize % config.getSnapshotBatchCount() == 0 || getDataSizeForSnapshotCheck() > dataThreshold;
}
@Override
- public void appendAndPersist(final ReplicatedLogEntry replicatedLogEntry) {
- appendAndPersist(replicatedLogEntry, null);
+ public void captureSnapshotIfReady(final ReplicatedLogEntry replicatedLogEntry) {
+ if (shouldCaptureSnapshot(replicatedLogEntry.getIndex())) {
+ boolean started = context.getSnapshotManager().capture(replicatedLogEntry,
+ context.getCurrentBehavior().getReplicatedToAllIndex());
+ if (started && !context.hasFollowers()) {
+ dataSizeSinceLastSnapshot = 0;
+ }
+ }
+ }
+
+ private long getDataSizeForSnapshotCheck() {
+ if (!context.hasFollowers()) {
+ // When we do not have followers we do not maintain an in-memory log
+ // due to this the journalSize will never become anything close to the
+ // snapshot batch count. In fact will mostly be 1.
+ // Similarly since the journal's dataSize depends on the entries in the
+ // journal the journal's dataSize will never reach a value close to the
+ // memory threshold.
+ // By maintaining the dataSize outside the journal we are tracking essentially
+ // what we have written to the disk however since we no longer are in
+ // need of doing a snapshot just for the sake of freeing up memory we adjust
+ // the real size of data by the DATA_SIZE_DIVIDER so that we do not snapshot as often
+ // as if we were maintaining a real snapshot
+ return dataSizeSinceLastSnapshot / DATA_SIZE_DIVIDER;
+ } else {
+ return dataSize();
+ }
}
@Override
- public void appendAndPersist(final ReplicatedLogEntry replicatedLogEntry,
- final Procedure<ReplicatedLogEntry> callback) {
+ public boolean appendAndPersist(@Nonnull final ReplicatedLogEntry replicatedLogEntry,
+ @Nullable final Procedure<ReplicatedLogEntry> callback, boolean doAsync) {
+
+ context.getLogger().debug("{}: Append log entry and persist {} ", context.getId(), replicatedLogEntry);
- if(context.getLogger().isDebugEnabled()) {
- context.getLogger().debug("{}: Append log entry and persist {} ", context.getId(), replicatedLogEntry);
+ if (!append(replicatedLogEntry)) {
+ return false;
}
- // FIXME : By adding the replicated log entry to the in-memory journal we are not truly ensuring durability of the logs
- journal.add(replicatedLogEntry);
-
- // When persisting events with persist it is guaranteed that the
- // persistent actor will not receive further commands between the
- // persist call and the execution(s) of the associated event
- // handler. This also holds for multiple persist calls in context
- // of a single command.
- persistence.persist(replicatedLogEntry,
- new Procedure<ReplicatedLogEntry>() {
- @Override
- public void apply(ReplicatedLogEntry evt) throws Exception {
- int logEntrySize = replicatedLogEntry.size();
-
- dataSize += logEntrySize;
- long dataSizeForCheck = dataSize;
-
- dataSizeSinceLastSnapshot += logEntrySize;
-
- if (!context.hasFollowers()) {
- // When we do not have followers we do not maintain an in-memory log
- // due to this the journalSize will never become anything close to the
- // snapshot batch count. In fact will mostly be 1.
- // Similarly since the journal's dataSize depends on the entries in the
- // journal the journal's dataSize will never reach a value close to the
- // memory threshold.
- // By maintaining the dataSize outside the journal we are tracking essentially
- // what we have written to the disk however since we no longer are in
- // need of doing a snapshot just for the sake of freeing up memory we adjust
- // the real size of data by the DATA_SIZE_DIVIDER so that we do not snapshot as often
- // as if we were maintaining a real snapshot
- dataSizeForCheck = dataSizeSinceLastSnapshot / DATA_SIZE_DIVIDER;
- }
- long journalSize = replicatedLogEntry.getIndex() + 1;
- long dataThreshold = context.getTotalMemory() *
- context.getConfigParams().getSnapshotDataThresholdPercentage() / 100;
-
- if ((journalSize % context.getConfigParams().getSnapshotBatchCount() == 0
- || dataSizeForCheck > dataThreshold)) {
-
- boolean started = context.getSnapshotManager().capture(replicatedLogEntry,
- currentBehavior.getReplicatedToAllIndex());
-
- if(started){
- dataSizeSinceLastSnapshot = 0;
- }
- }
-
- if (callback != null){
- callback.apply(replicatedLogEntry);
- }
- }
+ Procedure<ReplicatedLogEntry> persistCallback = persistedLogEntry -> {
+ context.getLogger().debug("{}: persist complete {}", context.getId(), persistedLogEntry);
+
+ dataSizeSinceLastSnapshot += persistedLogEntry.size();
+
+ if (callback != null) {
+ callback.apply(persistedLogEntry);
}
- );
+ };
+
+ if (doAsync) {
+ context.getPersistenceProvider().persistAsync(replicatedLogEntry, persistCallback);
+ } else {
+ context.getPersistenceProvider().persist(replicatedLogEntry, persistCallback);
+ }
+
+ return true;
}
-}
\ No newline at end of file
+}