Use local param in persist callback
[controller.git] / opendaylight / md-sal / sal-akka-raft / src / main / java / org / opendaylight / controller / cluster / raft / ReplicatedLogImpl.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.controller.cluster.raft;
9
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;
15 import org.opendaylight.controller.cluster.raft.behaviors.RaftActorBehavior;
16
17 /**
18  * Implementation of ReplicatedLog used by the RaftActor.
19  */
20 class ReplicatedLogImpl extends AbstractReplicatedLogImpl {
21     private static final int DATA_SIZE_DIVIDER = 5;
22
23     private long dataSizeSinceLastSnapshot = 0L;
24     private final RaftActorContext context;
25     private final RaftActorBehavior currentBehavior;
26
27     private final Procedure<DeleteEntries> deleteProcedure = new Procedure<DeleteEntries>() {
28         @Override
29         public void apply(final DeleteEntries notUsed) {
30         }
31     };
32
33     static ReplicatedLog newInstance(final Snapshot snapshot, final RaftActorContext context,
34             final RaftActorBehavior currentBehavior) {
35         return new ReplicatedLogImpl(snapshot.getLastAppliedIndex(), snapshot.getLastAppliedTerm(),
36                 snapshot.getUnAppliedEntries(), context, currentBehavior);
37     }
38
39     static ReplicatedLog newInstance(final RaftActorContext context, final RaftActorBehavior currentBehavior) {
40         return new ReplicatedLogImpl(-1L, -1L, Collections.<ReplicatedLogEntry>emptyList(), context,
41                 currentBehavior);
42     }
43
44     private ReplicatedLogImpl(final long snapshotIndex, final long snapshotTerm, final List<ReplicatedLogEntry> unAppliedEntries,
45             final RaftActorContext context, final RaftActorBehavior currentBehavior) {
46         super(snapshotIndex, snapshotTerm, unAppliedEntries);
47         this.context = Preconditions.checkNotNull(context);
48         this.currentBehavior = Preconditions.checkNotNull(currentBehavior);
49     }
50
51     @Override
52     public void removeFromAndPersist(final long logEntryIndex) {
53         // FIXME: Maybe this should be done after the command is saved
54         long adjustedIndex = removeFrom(logEntryIndex);
55         if(adjustedIndex >= 0) {
56             context.getPersistenceProvider().persist(new DeleteEntries(adjustedIndex), deleteProcedure);
57         }
58     }
59
60     @Override
61     public void appendAndPersist(final ReplicatedLogEntry replicatedLogEntry) {
62         appendAndPersist(replicatedLogEntry, null);
63     }
64
65     @Override
66     public void captureSnapshotIfReady(final ReplicatedLogEntry replicatedLogEntry) {
67         long journalSize = replicatedLogEntry.getIndex() + 1;
68         long dataThreshold = context.getTotalMemory() *
69                 context.getConfigParams().getSnapshotDataThresholdPercentage() / 100;
70
71         if ((journalSize % context.getConfigParams().getSnapshotBatchCount() == 0
72                 || getDataSizeForSnapshotCheck() > dataThreshold)) {
73
74             boolean started = context.getSnapshotManager().capture(replicatedLogEntry,
75                     currentBehavior.getReplicatedToAllIndex());
76             if (started) {
77                 if (!context.hasFollowers()) {
78                     dataSizeSinceLastSnapshot = 0;
79                 }
80             }
81         }
82     }
83
84     private long getDataSizeForSnapshotCheck() {
85         long dataSizeForCheck = dataSize();
86         if (!context.hasFollowers()) {
87             // When we do not have followers we do not maintain an in-memory log
88             // due to this the journalSize will never become anything close to the
89             // snapshot batch count. In fact will mostly be 1.
90             // Similarly since the journal's dataSize depends on the entries in the
91             // journal the journal's dataSize will never reach a value close to the
92             // memory threshold.
93             // By maintaining the dataSize outside the journal we are tracking essentially
94             // what we have written to the disk however since we no longer are in
95             // need of doing a snapshot just for the sake of freeing up memory we adjust
96             // the real size of data by the DATA_SIZE_DIVIDER so that we do not snapshot as often
97             // as if we were maintaining a real snapshot
98             dataSizeForCheck = dataSizeSinceLastSnapshot / DATA_SIZE_DIVIDER;
99         }
100         return dataSizeForCheck;
101     }
102
103     @Override
104     public void appendAndPersist(final ReplicatedLogEntry replicatedLogEntry,
105             final Procedure<ReplicatedLogEntry> callback)  {
106
107         if (context.getLogger().isDebugEnabled()) {
108             context.getLogger().debug("{}: Append log entry and persist {} ", context.getId(), replicatedLogEntry);
109         }
110
111         // FIXME : By adding the replicated log entry to the in-memory journal we are not truly ensuring durability of the logs
112         append(replicatedLogEntry);
113
114         // When persisting events with persist it is guaranteed that the
115         // persistent actor will not receive further commands between the
116         // persist call and the execution(s) of the associated event
117         // handler. This also holds for multiple persist calls in context
118         // of a single command.
119         context.getPersistenceProvider().persist(replicatedLogEntry,
120             new Procedure<ReplicatedLogEntry>() {
121                 @Override
122                 public void apply(final ReplicatedLogEntry param) throws Exception {
123                     context.getLogger().debug("{}: persist complete {}", context.getId(), param);
124
125                     int logEntrySize = param.size();
126                     dataSizeSinceLastSnapshot += logEntrySize;
127
128                     if (callback != null) {
129                         callback.apply(param);
130                     }
131                 }
132             }
133         );
134     }
135 }