8adca722a2d6a887ee37545a9b3c488f5e4c6996
[controller.git] / opendaylight / md-sal / sal-akka-raft / src / main / java / org / opendaylight / controller / cluster / raft / behaviors / SyncStatusTracker.java
1 /*
2  * Copyright (c) 2015 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.behaviors;
10
11 import akka.actor.ActorRef;
12 import com.google.common.base.Preconditions;
13 import org.opendaylight.controller.cluster.raft.base.messages.FollowerInitialSyncUpStatus;
14 import org.slf4j.Logger;
15 import org.slf4j.LoggerFactory;
16
17 /**
18  * The SyncStatusTracker tracks if a Follower is in sync with any given Leader or not
19  * When an update is received from the Leader and the update happens to be the first update
20  * from that Leader then the SyncStatusTracker will not mark the Follower as not in-sync till the
21  * Followers commitIndex matches the commitIndex that the Leader sent in it's very first update.
22  * Subsequently when an update is received the tracker will consider the Follower to be out of
23  * sync if it is behind by 'syncThreshold' commits.
24  */
25 public class SyncStatusTracker {
26     private static final Logger LOG = LoggerFactory.getLogger(SyncStatusTracker.class);
27
28     private static final boolean IN_SYNC = true;
29     private static final boolean NOT_IN_SYNC = false;
30
31     private final String id;
32     private final ActorRef actor;
33     private final int syncThreshold;
34
35     // FIXME: what is this magic constant?
36     private long minimumExpectedIndex = -2L;
37     private String syncedLeaderId = null;
38     private boolean syncStatus = false;
39
40     public SyncStatusTracker(final ActorRef actor, final String id, final int syncThreshold) {
41         this.actor = Preconditions.checkNotNull(actor, "actor should not be null");
42         this.id = Preconditions.checkNotNull(id, "id should not be null");
43         Preconditions.checkArgument(syncThreshold >= 0, "syncThreshold should be greater than or equal to 0");
44         this.syncThreshold = syncThreshold;
45     }
46
47     public void update(final String leaderId, final long leaderCommit, final long commitIndex) {
48         Preconditions.checkNotNull(leaderId, "leaderId should not be null");
49
50         if (!leaderId.equals(syncedLeaderId)) {
51             minimumExpectedIndex = leaderCommit;
52             LOG.debug("Last sync leader {} does not match current leader {}, need to catch up to {}", syncedLeaderId,
53                 leaderId, leaderCommit);
54             changeSyncStatus(NOT_IN_SYNC, true);
55             syncedLeaderId = leaderId;
56             return;
57         }
58
59         final long lag = leaderCommit - commitIndex;
60         if (lag > syncThreshold) {
61             LOG.debug("Lagging {} entries behind leader {}", lag, leaderId);
62             changeSyncStatus(NOT_IN_SYNC, false);
63         } else if (commitIndex >= minimumExpectedIndex) {
64             LOG.debug("Lagging {} entries behind leader and reached {} (of expected {})", lag, leaderId, commitIndex,
65                 minimumExpectedIndex);
66             changeSyncStatus(IN_SYNC, false);
67         }
68     }
69
70     private void changeSyncStatus(final boolean newSyncStatus, final boolean forceStatusChange) {
71         if (forceStatusChange || newSyncStatus != syncStatus) {
72             actor.tell(new FollowerInitialSyncUpStatus(newSyncStatus, id), ActorRef.noSender());
73             syncStatus = newSyncStatus;
74         } else {
75             LOG.trace("No change in sync status of {}, dampening message", actor);
76         }
77     }
78 }