Bug 6540: Refactor FollowerToSnapshot to its own class
[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.Preconditions;
12 import com.google.common.base.Stopwatch;
13 import java.util.concurrent.TimeUnit;
14
15 public class FollowerLogInformationImpl implements FollowerLogInformation {
16     private final Stopwatch stopwatch = Stopwatch.createUnstarted();
17
18     private final RaftActorContext context;
19
20     private long nextIndex;
21
22     private long matchIndex;
23
24     private long lastReplicatedIndex = -1L;
25
26     private final Stopwatch lastReplicatedStopwatch = Stopwatch.createUnstarted();
27
28     private short payloadVersion = -1;
29
30     // Assume the HELIUM_VERSION version initially for backwards compatibility until we obtain the follower's
31     // actual version via AppendEntriesReply. Although we no longer support the Helium version, a pre-Boron
32     // follower will not have the version field in AppendEntriesReply so it will be set to 0 which is
33     // HELIUM_VERSION.
34     private short raftVersion = RaftVersions.HELIUM_VERSION;
35
36     private final PeerInfo peerInfo;
37
38     public FollowerLogInformationImpl(PeerInfo peerInfo, long matchIndex, RaftActorContext context) {
39         this.nextIndex = context.getCommitIndex();
40         this.matchIndex = matchIndex;
41         this.context = context;
42         this.peerInfo = Preconditions.checkNotNull(peerInfo);
43     }
44
45     @Override
46     public long incrNextIndex() {
47         return nextIndex++;
48     }
49
50     @Override
51     public long decrNextIndex() {
52         return nextIndex--;
53     }
54
55     @Override
56     public boolean setNextIndex(long nextIndex) {
57         if(this.nextIndex != nextIndex) {
58             this.nextIndex = nextIndex;
59             return true;
60         }
61
62         return false;
63     }
64
65     @Override
66     public long incrMatchIndex(){
67         return matchIndex++;
68     }
69
70     @Override
71     public boolean setMatchIndex(long matchIndex) {
72         if(this.matchIndex != matchIndex) {
73             this.matchIndex = matchIndex;
74             return true;
75         }
76
77         return false;
78     }
79
80     @Override
81     public String getId() {
82         return peerInfo.getId();
83     }
84
85     @Override
86     public long getNextIndex() {
87         return nextIndex;
88     }
89
90     @Override
91     public long getMatchIndex() {
92         return matchIndex;
93     }
94
95     @Override
96     public boolean isFollowerActive() {
97         if(peerInfo.getVotingState() == VotingState.VOTING_NOT_INITIALIZED) {
98             return false;
99         }
100
101         long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS);
102         return (stopwatch.isRunning()) &&
103                 (elapsed <= context.getConfigParams().getElectionTimeOutInterval().toMillis());
104     }
105
106     @Override
107     public void markFollowerActive() {
108         if (stopwatch.isRunning()) {
109             stopwatch.reset();
110         }
111         stopwatch.start();
112     }
113
114     @Override
115     public void markFollowerInActive() {
116         if (stopwatch.isRunning()) {
117             stopwatch.stop();
118         }
119     }
120
121     @Override
122     public long timeSinceLastActivity() {
123         return stopwatch.elapsed(TimeUnit.MILLISECONDS);
124     }
125
126     @Override
127     public boolean okToReplicate() {
128         if(peerInfo.getVotingState() == VotingState.VOTING_NOT_INITIALIZED) {
129             return false;
130         }
131
132         // Return false if we are trying to send duplicate data before the heartbeat interval
133         if(getNextIndex() == lastReplicatedIndex){
134             if(lastReplicatedStopwatch.elapsed(TimeUnit.MILLISECONDS) < context.getConfigParams()
135                     .getHeartBeatInterval().toMillis()){
136                 return false;
137             }
138         }
139
140         resetLastReplicated();
141         return true;
142     }
143
144     private void resetLastReplicated(){
145         lastReplicatedIndex = getNextIndex();
146         if(lastReplicatedStopwatch.isRunning()){
147             lastReplicatedStopwatch.reset();
148         }
149         lastReplicatedStopwatch.start();
150     }
151
152     @Override
153     public short getPayloadVersion() {
154         return payloadVersion;
155     }
156
157     @Override
158     public void setPayloadVersion(short payloadVersion) {
159         this.payloadVersion = payloadVersion;
160     }
161
162     @Override
163     public short getRaftVersion() {
164         return raftVersion;
165     }
166
167     @Override
168     public void setRaftVersion(short raftVersion) {
169         this.raftVersion = raftVersion;
170     }
171
172     @Override
173     public String toString() {
174         return "FollowerLogInformationImpl [id=" + getId() + ", nextIndex=" + nextIndex + ", matchIndex=" + matchIndex
175                 + ", lastReplicatedIndex=" + lastReplicatedIndex + ", votingState=" + peerInfo.getVotingState()
176                 + ", stopwatch=" + stopwatch.elapsed(TimeUnit.MILLISECONDS) + ", followerTimeoutMillis="
177                 + context.getConfigParams().getElectionTimeOutInterval().toMillis() + "]";
178     }
179 }