2 * Copyright (c) 2014 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
9 package org.opendaylight.controller.cluster.raft;
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;
19 * Implementation of the FollowerLogInformation interface.
22 * @author Thomas Pantelis
24 public class FollowerLogInformationImpl implements FollowerLogInformation {
25 private final Stopwatch stopwatch = Stopwatch.createUnstarted();
27 private final RaftActorContext context;
29 private long nextIndex;
31 private long matchIndex;
33 private long lastReplicatedIndex = -1L;
35 private final Stopwatch lastReplicatedStopwatch = Stopwatch.createUnstarted();
37 private short payloadVersion = -1;
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
43 private short raftVersion = RaftVersions.HELIUM_VERSION;
45 private final PeerInfo peerInfo;
47 private LeaderInstallSnapshotState installSnapshotState;
49 private long slicedLogEntryIndex = NO_INDEX;
52 * Constructs an instance.
54 * @param peerInfo the associated PeerInfo of the follower.
55 * @param matchIndex the initial match index.
56 * @param context the RaftActorContext.
58 public FollowerLogInformationImpl(PeerInfo peerInfo, long matchIndex, RaftActorContext context) {
59 this.nextIndex = context.getCommitIndex();
60 this.matchIndex = matchIndex;
61 this.context = context;
62 this.peerInfo = Preconditions.checkNotNull(peerInfo);
66 public long incrNextIndex() {
71 public boolean decrNextIndex() {
81 public boolean setNextIndex(long nextIndex) {
82 if (this.nextIndex != nextIndex) {
83 this.nextIndex = nextIndex;
91 public long incrMatchIndex() {
96 public boolean setMatchIndex(long matchIndex) {
97 // If the new match index is the index of the entry currently being sliced, then we know slicing is complete
98 // and the follower received the entry and responded so clear the slicedLogEntryIndex
99 if (isLogEntrySlicingInProgress() && slicedLogEntryIndex == matchIndex) {
100 slicedLogEntryIndex = NO_INDEX;
103 if (this.matchIndex != matchIndex) {
104 this.matchIndex = matchIndex;
112 public String getId() {
113 return peerInfo.getId();
117 public long getNextIndex() {
122 public long getMatchIndex() {
127 public boolean isFollowerActive() {
128 if (peerInfo.getVotingState() == VotingState.VOTING_NOT_INITIALIZED) {
132 long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS);
133 return stopwatch.isRunning()
134 && elapsed <= context.getConfigParams().getElectionTimeOutInterval().toMillis();
138 public void markFollowerActive() {
139 if (stopwatch.isRunning()) {
146 public void markFollowerInActive() {
147 if (stopwatch.isRunning()) {
153 public long timeSinceLastActivity() {
154 return stopwatch.elapsed(TimeUnit.MILLISECONDS);
158 public boolean okToReplicate() {
159 if (peerInfo.getVotingState() == VotingState.VOTING_NOT_INITIALIZED) {
163 // Return false if we are trying to send duplicate data before the heartbeat interval
164 if (getNextIndex() == lastReplicatedIndex && lastReplicatedStopwatch.elapsed(TimeUnit.MILLISECONDS)
165 < context.getConfigParams().getHeartBeatInterval().toMillis()) {
169 resetLastReplicated();
173 private void resetLastReplicated() {
174 lastReplicatedIndex = getNextIndex();
175 if (lastReplicatedStopwatch.isRunning()) {
176 lastReplicatedStopwatch.reset();
178 lastReplicatedStopwatch.start();
182 public short getPayloadVersion() {
183 return payloadVersion;
187 public void setPayloadVersion(short payloadVersion) {
188 this.payloadVersion = payloadVersion;
192 public short getRaftVersion() {
197 public void setRaftVersion(short raftVersion) {
198 this.raftVersion = raftVersion;
203 public LeaderInstallSnapshotState getInstallSnapshotState() {
204 return installSnapshotState;
208 public void setLeaderInstallSnapshotState(@Nonnull LeaderInstallSnapshotState state) {
209 if (this.installSnapshotState == null) {
210 this.installSnapshotState = Preconditions.checkNotNull(state);
215 public void clearLeaderInstallSnapshotState() {
216 Preconditions.checkState(installSnapshotState != null);
217 installSnapshotState.close();
218 installSnapshotState = null;
222 public void setSlicedLogEntryIndex(long index) {
223 slicedLogEntryIndex = index;
227 public boolean isLogEntrySlicingInProgress() {
228 return slicedLogEntryIndex != NO_INDEX;
232 public String toString() {
233 return "FollowerLogInformationImpl [id=" + getId() + ", nextIndex=" + nextIndex + ", matchIndex=" + matchIndex
234 + ", lastReplicatedIndex=" + lastReplicatedIndex + ", votingState=" + peerInfo.getVotingState()
235 + ", stopwatch=" + stopwatch.elapsed(TimeUnit.MILLISECONDS) + ", followerTimeoutMillis="
236 + context.getConfigParams().getElectionTimeOutInterval().toMillis() + "]";