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 org.opendaylight.controller.cluster.raft.base.messages.DeleteEntries;
17 * Implementation of ReplicatedLog used by the RaftActor.
19 class ReplicatedLogImpl extends AbstractReplicatedLogImpl {
20 private static final int DATA_SIZE_DIVIDER = 5;
22 private final Procedure<DeleteEntries> deleteProcedure = new Procedure<DeleteEntries>() {
24 public void apply(final DeleteEntries notUsed) {
28 private final RaftActorContext context;
29 private long dataSizeSinceLastSnapshot = 0L;
31 private ReplicatedLogImpl(final long snapshotIndex, final long snapshotTerm, final List<ReplicatedLogEntry> unAppliedEntries,
32 final RaftActorContext context) {
33 super(snapshotIndex, snapshotTerm, unAppliedEntries);
34 this.context = Preconditions.checkNotNull(context);
37 static ReplicatedLog newInstance(final Snapshot snapshot, final RaftActorContext context) {
38 return new ReplicatedLogImpl(snapshot.getLastAppliedIndex(), snapshot.getLastAppliedTerm(),
39 snapshot.getUnAppliedEntries(), context);
42 static ReplicatedLog newInstance(final RaftActorContext context) {
43 return new ReplicatedLogImpl(-1L, -1L, Collections.<ReplicatedLogEntry>emptyList(), context);
47 public void removeFromAndPersist(final long logEntryIndex) {
48 // FIXME: Maybe this should be done after the command is saved
49 long adjustedIndex = removeFrom(logEntryIndex);
50 if(adjustedIndex >= 0) {
51 context.getPersistenceProvider().persist(new DeleteEntries(adjustedIndex), deleteProcedure);
56 public void appendAndPersist(final ReplicatedLogEntry replicatedLogEntry) {
57 appendAndPersist(replicatedLogEntry, null);
61 public void captureSnapshotIfReady(final ReplicatedLogEntry replicatedLogEntry) {
62 final ConfigParams config = context.getConfigParams();
63 final long journalSize = replicatedLogEntry.getIndex() + 1;
64 final long dataThreshold = context.getTotalMemory() * config.getSnapshotDataThresholdPercentage() / 100;
66 if (journalSize % config.getSnapshotBatchCount() == 0 || getDataSizeForSnapshotCheck() > dataThreshold) {
67 boolean started = context.getSnapshotManager().capture(replicatedLogEntry,
68 context.getCurrentBehavior().getReplicatedToAllIndex());
69 if (started && !context.hasFollowers()) {
70 dataSizeSinceLastSnapshot = 0;
75 private long getDataSizeForSnapshotCheck() {
76 if (!context.hasFollowers()) {
77 // When we do not have followers we do not maintain an in-memory log
78 // due to this the journalSize will never become anything close to the
79 // snapshot batch count. In fact will mostly be 1.
80 // Similarly since the journal's dataSize depends on the entries in the
81 // journal the journal's dataSize will never reach a value close to the
83 // By maintaining the dataSize outside the journal we are tracking essentially
84 // what we have written to the disk however since we no longer are in
85 // need of doing a snapshot just for the sake of freeing up memory we adjust
86 // the real size of data by the DATA_SIZE_DIVIDER so that we do not snapshot as often
87 // as if we were maintaining a real snapshot
88 return dataSizeSinceLastSnapshot / DATA_SIZE_DIVIDER;
95 public void appendAndPersist(final ReplicatedLogEntry replicatedLogEntry,
96 final Procedure<ReplicatedLogEntry> callback) {
98 context.getLogger().debug("{}: Append log entry and persist {} ", context.getId(), replicatedLogEntry);
100 // FIXME : By adding the replicated log entry to the in-memory journal we are not truly ensuring durability of the logs
101 append(replicatedLogEntry);
103 // When persisting events with persist it is guaranteed that the
104 // persistent actor will not receive further commands between the
105 // persist call and the execution(s) of the associated event
106 // handler. This also holds for multiple persist calls in context
107 // of a single command.
108 context.getPersistenceProvider().persist(replicatedLogEntry,
109 new Procedure<ReplicatedLogEntry>() {
111 public void apply(final ReplicatedLogEntry param) throws Exception {
112 context.getLogger().debug("{}: persist complete {}", context.getId(), param);
114 int logEntrySize = param.size();
115 dataSizeSinceLastSnapshot += logEntrySize;
117 if (callback != null) {
118 callback.apply(param);