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 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;
18 * Implementation of ReplicatedLog used by the RaftActor.
20 class ReplicatedLogImpl extends AbstractReplicatedLogImpl {
21 private static final int DATA_SIZE_DIVIDER = 5;
23 private long dataSizeSinceLastSnapshot = 0L;
24 private final RaftActorContext context;
25 private final DataPersistenceProvider persistence;
26 private final RaftActorBehavior currentBehavior;
28 private final Procedure<DeleteEntries> deleteProcedure = new Procedure<DeleteEntries>() {
30 public void apply(DeleteEntries param) {
32 for (ReplicatedLogEntry entry : journal) {
33 dataSize += entry.size();
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);
44 static ReplicatedLog newInstance(RaftActorContext context,
45 DataPersistenceProvider persistence, RaftActorBehavior currentBehavior) {
46 return new ReplicatedLogImpl(-1L, -1L, Collections.<ReplicatedLogEntry>emptyList(), context,
47 persistence, currentBehavior);
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;
59 public void removeFromAndPersist(long logEntryIndex) {
60 int adjustedIndex = adjustedIndex(logEntryIndex);
62 if (adjustedIndex < 0) {
66 // FIXME: Maybe this should be done after the command is saved
67 journal.subList(adjustedIndex , journal.size()).clear();
69 persistence.persist(new DeleteEntries(adjustedIndex), deleteProcedure);
73 public void appendAndPersist(final ReplicatedLogEntry replicatedLogEntry) {
74 appendAndPersist(replicatedLogEntry, null);
78 public void appendAndPersist(final ReplicatedLogEntry replicatedLogEntry,
79 final Procedure<ReplicatedLogEntry> callback) {
81 if(context.getLogger().isDebugEnabled()) {
82 context.getLogger().debug("{}: Append log entry and persist {} ", context.getId(), replicatedLogEntry);
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);
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>() {
96 public void apply(ReplicatedLogEntry evt) throws Exception {
97 int logEntrySize = replicatedLogEntry.size();
99 dataSize += logEntrySize;
100 long dataSizeForCheck = dataSize;
102 dataSizeSinceLastSnapshot += logEntrySize;
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
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;
118 long journalSize = replicatedLogEntry.getIndex() + 1;
119 long dataThreshold = context.getTotalMemory() *
120 context.getConfigParams().getSnapshotDataThresholdPercentage() / 100;
122 if ((journalSize % context.getConfigParams().getSnapshotBatchCount() == 0
123 || dataSizeForCheck > dataThreshold)) {
125 boolean started = context.getSnapshotManager().capture(replicatedLogEntry,
126 currentBehavior.getReplicatedToAllIndex());
129 dataSizeSinceLastSnapshot = 0;
133 if (callback != null){
134 callback.apply(replicatedLogEntry);