15063cff5b4a8a9c0114dbad73709f27172fc050
[controller.git] / opendaylight / md-sal / sal-akka-raft / src / main / java / org / opendaylight / controller / cluster / raft / FollowerLogInformationImpl.java
1 /*
2  * Copyright (c) 2014 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
9 package org.opendaylight.controller.cluster.raft;
10
11 import com.google.common.base.Stopwatch;
12 import java.util.concurrent.TimeUnit;
13 import java.util.concurrent.atomic.AtomicLongFieldUpdater;
14
15 public class FollowerLogInformationImpl implements FollowerLogInformation {
16     private static final AtomicLongFieldUpdater<FollowerLogInformationImpl> NEXT_INDEX_UPDATER = AtomicLongFieldUpdater.newUpdater(FollowerLogInformationImpl.class, "nextIndex");
17     private static final AtomicLongFieldUpdater<FollowerLogInformationImpl> MATCH_INDEX_UPDATER = AtomicLongFieldUpdater.newUpdater(FollowerLogInformationImpl.class, "matchIndex");
18
19     private final String id;
20
21     private final Stopwatch stopwatch = Stopwatch.createUnstarted();
22
23     private final RaftActorContext context;
24
25     private volatile long nextIndex;
26
27     private volatile long matchIndex;
28
29     private long lastReplicatedIndex = -1L;
30
31     private final Stopwatch lastReplicatedStopwatch = Stopwatch.createUnstarted();
32
33
34     public FollowerLogInformationImpl(String id, long matchIndex, RaftActorContext context) {
35         this.id = id;
36         this.nextIndex = context.getCommitIndex();
37         this.matchIndex = matchIndex;
38         this.context = context;
39     }
40
41     @Override
42     public long incrNextIndex(){
43         return NEXT_INDEX_UPDATER.incrementAndGet(this);
44     }
45
46     @Override
47     public long decrNextIndex() {
48         return NEXT_INDEX_UPDATER.decrementAndGet(this);
49     }
50
51     @Override
52     public boolean setNextIndex(long nextIndex) {
53         if(this.nextIndex != nextIndex) {
54             this.nextIndex = nextIndex;
55             return true;
56         }
57
58         return false;
59     }
60
61     @Override
62     public long incrMatchIndex(){
63         return MATCH_INDEX_UPDATER.incrementAndGet(this);
64     }
65
66     @Override
67     public boolean setMatchIndex(long matchIndex) {
68         if(this.matchIndex != matchIndex) {
69             this.matchIndex = matchIndex;
70             return true;
71         }
72
73         return false;
74     }
75
76     @Override
77     public String getId() {
78         return id;
79     }
80
81     @Override
82     public long getNextIndex() {
83         return nextIndex;
84     }
85
86     @Override
87     public long getMatchIndex() {
88         return matchIndex;
89     }
90
91     @Override
92     public boolean isFollowerActive() {
93         long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS);
94         return (stopwatch.isRunning()) &&
95                 (elapsed <= context.getConfigParams().getElectionTimeOutInterval().toMillis());
96     }
97
98     @Override
99     public void markFollowerActive() {
100         if (stopwatch.isRunning()) {
101             stopwatch.reset();
102         }
103         stopwatch.start();
104     }
105
106     @Override
107     public void markFollowerInActive() {
108         if (stopwatch.isRunning()) {
109             stopwatch.stop();
110         }
111     }
112
113     @Override
114     public long timeSinceLastActivity() {
115         return stopwatch.elapsed(TimeUnit.MILLISECONDS);
116     }
117
118     @Override
119     public boolean okToReplicate() {
120         // Return false if we are trying to send duplicate data before the heartbeat interval
121         if(getNextIndex() == lastReplicatedIndex){
122             if(lastReplicatedStopwatch.elapsed(TimeUnit.MILLISECONDS) < context.getConfigParams()
123                     .getHeartBeatInterval().toMillis()){
124                 return false;
125             }
126         }
127
128         resetLastReplicated();
129         return true;
130     }
131
132     private void resetLastReplicated(){
133         lastReplicatedIndex = getNextIndex();
134         if(lastReplicatedStopwatch.isRunning()){
135             lastReplicatedStopwatch.reset();
136         }
137         lastReplicatedStopwatch.start();
138     }
139
140     @Override
141     public String toString() {
142         StringBuilder builder = new StringBuilder();
143         builder.append("FollowerLogInformationImpl [id=").append(id).append(", nextIndex=").append(nextIndex)
144                 .append(", matchIndex=").append(matchIndex).append(", stopwatch=")
145                 .append(stopwatch.elapsed(TimeUnit.MILLISECONDS))
146                 .append(", followerTimeoutMillis=")
147                 .append(context.getConfigParams().getElectionTimeOutInterval().toMillis()).append("]");
148         return builder.toString();
149     }
150 }