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