2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.controller.cluster.raft;
10 import akka.japi.Procedure;
11 import com.google.common.base.Preconditions;
12 import java.util.Collections;
13 import java.util.List;
14 import javax.annotation.Nonnull;
15 import javax.annotation.Nullable;
16 import org.opendaylight.controller.cluster.raft.persisted.DeleteEntries;
17 import org.opendaylight.controller.cluster.raft.persisted.Snapshot;
20 * Implementation of ReplicatedLog used by the RaftActor.
22 class ReplicatedLogImpl extends AbstractReplicatedLogImpl {
23 private static final int DATA_SIZE_DIVIDER = 5;
25 private final RaftActorContext context;
26 private long dataSizeSinceLastSnapshot = 0L;
28 private ReplicatedLogImpl(final long snapshotIndex, final long snapshotTerm,
29 final List<ReplicatedLogEntry> unAppliedEntries,
30 final RaftActorContext context) {
31 super(snapshotIndex, snapshotTerm, unAppliedEntries, context.getId());
32 this.context = Preconditions.checkNotNull(context);
35 static ReplicatedLog newInstance(final Snapshot snapshot, final RaftActorContext context) {
36 return new ReplicatedLogImpl(snapshot.getLastAppliedIndex(), snapshot.getLastAppliedTerm(),
37 snapshot.getUnAppliedEntries(), context);
40 static ReplicatedLog newInstance(final RaftActorContext context) {
41 return new ReplicatedLogImpl(-1L, -1L, Collections.<ReplicatedLogEntry>emptyList(), context);
45 public boolean removeFromAndPersist(final long logEntryIndex) {
46 // FIXME: Maybe this should be done after the command is saved
47 long adjustedIndex = removeFrom(logEntryIndex);
48 if (adjustedIndex >= 0) {
49 context.getPersistenceProvider().persist(new DeleteEntries(adjustedIndex), NoopProcedure.instance());
57 public boolean shouldCaptureSnapshot(long logIndex) {
58 final ConfigParams config = context.getConfigParams();
59 final long journalSize = logIndex + 1;
60 final long dataThreshold = context.getTotalMemory() * config.getSnapshotDataThresholdPercentage() / 100;
62 return journalSize % config.getSnapshotBatchCount() == 0 || getDataSizeForSnapshotCheck() > dataThreshold;
66 public void captureSnapshotIfReady(final ReplicatedLogEntry replicatedLogEntry) {
67 if (shouldCaptureSnapshot(replicatedLogEntry.getIndex())) {
68 boolean started = context.getSnapshotManager().capture(replicatedLogEntry,
69 context.getCurrentBehavior().getReplicatedToAllIndex());
70 if (started && !context.hasFollowers()) {
71 dataSizeSinceLastSnapshot = 0;
76 private long getDataSizeForSnapshotCheck() {
77 if (!context.hasFollowers()) {
78 // When we do not have followers we do not maintain an in-memory log
79 // due to this the journalSize will never become anything close to the
80 // snapshot batch count. In fact will mostly be 1.
81 // Similarly since the journal's dataSize depends on the entries in the
82 // journal the journal's dataSize will never reach a value close to the
84 // By maintaining the dataSize outside the journal we are tracking essentially
85 // what we have written to the disk however since we no longer are in
86 // need of doing a snapshot just for the sake of freeing up memory we adjust
87 // the real size of data by the DATA_SIZE_DIVIDER so that we do not snapshot as often
88 // as if we were maintaining a real snapshot
89 return dataSizeSinceLastSnapshot / DATA_SIZE_DIVIDER;
96 public boolean appendAndPersist(@Nonnull final ReplicatedLogEntry replicatedLogEntry,
97 @Nullable final Procedure<ReplicatedLogEntry> callback, boolean doAsync) {
99 context.getLogger().debug("{}: Append log entry and persist {} ", context.getId(), replicatedLogEntry);
101 if (!append(replicatedLogEntry)) {
105 Procedure<ReplicatedLogEntry> persistCallback = persistedLogEntry -> {
106 context.getLogger().debug("{}: persist complete {}", context.getId(), persistedLogEntry);
108 dataSizeSinceLastSnapshot += persistedLogEntry.size();
110 if (callback != null) {
111 callback.apply(persistedLogEntry);
116 context.getPersistenceProvider().persistAsync(replicatedLogEntry, persistCallback);
118 context.getPersistenceProvider().persist(replicatedLogEntry, persistCallback);