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