883bfbb4e4184f0cbf0843002dfa3498ca13f622
[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 import javax.annotation.Nonnull;
15 import javax.annotation.Nullable;
16 import org.opendaylight.controller.cluster.raft.behaviors.LeaderInstallSnapshotState;
17
18 /**
19  * Implementation of the FollowerLogInformation interface.
20  *
21  * @author Moiz Raja
22  * @author Thomas Pantelis
23  */
24 public class FollowerLogInformationImpl implements FollowerLogInformation {
25     private final Stopwatch stopwatch = Stopwatch.createUnstarted();
26
27     private final RaftActorContext context;
28
29     private long nextIndex;
30
31     private long matchIndex;
32
33     private long lastReplicatedIndex = -1L;
34
35     private final Stopwatch lastReplicatedStopwatch = Stopwatch.createUnstarted();
36
37     private short payloadVersion = -1;
38
39     // Assume the HELIUM_VERSION version initially for backwards compatibility until we obtain the follower's
40     // actual version via AppendEntriesReply. Although we no longer support the Helium version, a pre-Boron
41     // follower will not have the version field in AppendEntriesReply so it will be set to 0 which is
42     // HELIUM_VERSION.
43     private short raftVersion = RaftVersions.HELIUM_VERSION;
44
45     private final PeerInfo peerInfo;
46
47     private LeaderInstallSnapshotState installSnapshotState;
48
49     /**
50      * Constructs an instance.
51      *
52      * @param peerInfo the associated PeerInfo of the follower.
53      * @param matchIndex the initial match index.
54      * @param context the RaftActorContext.
55      */
56     public FollowerLogInformationImpl(PeerInfo peerInfo, long matchIndex, RaftActorContext context) {
57         this.nextIndex = context.getCommitIndex();
58         this.matchIndex = matchIndex;
59         this.context = context;
60         this.peerInfo = Preconditions.checkNotNull(peerInfo);
61     }
62
63     @Override
64     public long incrNextIndex() {
65         return nextIndex++;
66     }
67
68     @Override
69     public boolean decrNextIndex() {
70         if (nextIndex >= 0) {
71             nextIndex--;
72             return true;
73         }
74
75         return false;
76     }
77
78     @Override
79     public boolean setNextIndex(long nextIndex) {
80         if (this.nextIndex != nextIndex) {
81             this.nextIndex = nextIndex;
82             return true;
83         }
84
85         return false;
86     }
87
88     @Override
89     public long incrMatchIndex() {
90         return matchIndex++;
91     }
92
93     @Override
94     public boolean setMatchIndex(long matchIndex) {
95         if (this.matchIndex != matchIndex) {
96             this.matchIndex = matchIndex;
97             return true;
98         }
99
100         return false;
101     }
102
103     @Override
104     public String getId() {
105         return peerInfo.getId();
106     }
107
108     @Override
109     public long getNextIndex() {
110         return nextIndex;
111     }
112
113     @Override
114     public long getMatchIndex() {
115         return matchIndex;
116     }
117
118     @Override
119     public boolean isFollowerActive() {
120         if (peerInfo.getVotingState() == VotingState.VOTING_NOT_INITIALIZED) {
121             return false;
122         }
123
124         long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS);
125         return stopwatch.isRunning()
126                 && elapsed <= context.getConfigParams().getElectionTimeOutInterval().toMillis();
127     }
128
129     @Override
130     public void markFollowerActive() {
131         if (stopwatch.isRunning()) {
132             stopwatch.reset();
133         }
134         stopwatch.start();
135     }
136
137     @Override
138     public void markFollowerInActive() {
139         if (stopwatch.isRunning()) {
140             stopwatch.stop();
141         }
142     }
143
144     @Override
145     public long timeSinceLastActivity() {
146         return stopwatch.elapsed(TimeUnit.MILLISECONDS);
147     }
148
149     @Override
150     public boolean okToReplicate() {
151         if (peerInfo.getVotingState() == VotingState.VOTING_NOT_INITIALIZED) {
152             return false;
153         }
154
155         // Return false if we are trying to send duplicate data before the heartbeat interval
156         if (getNextIndex() == lastReplicatedIndex && lastReplicatedStopwatch.elapsed(TimeUnit.MILLISECONDS)
157                 < context.getConfigParams().getHeartBeatInterval().toMillis()) {
158             return false;
159         }
160
161         resetLastReplicated();
162         return true;
163     }
164
165     private void resetLastReplicated() {
166         lastReplicatedIndex = getNextIndex();
167         if (lastReplicatedStopwatch.isRunning()) {
168             lastReplicatedStopwatch.reset();
169         }
170         lastReplicatedStopwatch.start();
171     }
172
173     @Override
174     public short getPayloadVersion() {
175         return payloadVersion;
176     }
177
178     @Override
179     public void setPayloadVersion(short payloadVersion) {
180         this.payloadVersion = payloadVersion;
181     }
182
183     @Override
184     public short getRaftVersion() {
185         return raftVersion;
186     }
187
188     @Override
189     public void setRaftVersion(short raftVersion) {
190         this.raftVersion = raftVersion;
191     }
192
193     @Override
194     @Nullable
195     public LeaderInstallSnapshotState getInstallSnapshotState() {
196         return installSnapshotState;
197     }
198
199     @Override
200     public void setLeaderInstallSnapshotState(@Nonnull LeaderInstallSnapshotState state) {
201         if (this.installSnapshotState == null) {
202             this.installSnapshotState = Preconditions.checkNotNull(state);
203         }
204     }
205
206     @Override
207     public void clearLeaderInstallSnapshotState() {
208         Preconditions.checkState(installSnapshotState != null);
209         installSnapshotState.close();
210         installSnapshotState = null;
211     }
212
213     @Override
214     public String toString() {
215         return "FollowerLogInformationImpl [id=" + getId() + ", nextIndex=" + nextIndex + ", matchIndex=" + matchIndex
216                 + ", lastReplicatedIndex=" + lastReplicatedIndex + ", votingState=" + peerInfo.getVotingState()
217                 + ", stopwatch=" + stopwatch.elapsed(TimeUnit.MILLISECONDS) + ", followerTimeoutMillis="
218                 + context.getConfigParams().getElectionTimeOutInterval().toMillis() + "]";
219     }
220 }