Introduce candidate election timeout divisor 63/80663/2
authorRobert Varga <robert.varga@pantheon.tech>
Fri, 1 Mar 2019 20:45:48 +0000 (21:45 +0100)
committerTom Pantelis <tompantelis@gmail.com>
Mon, 4 Mar 2019 13:19:38 +0000 (13:19 +0000)
When a candidate is waiting for an election to occur, it is sometimes
preferable to shorten the timeout interval down, so that we do not
wait the full election timeout.

This patch introduces the knob which serves to divide the normal
timeout -- i.e. it can be halved.

Change-Id: Idbe81c39bf7aecf56b9f13a242bea89a1a4ac4f1
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/ConfigParams.java
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/DefaultConfigParamsImpl.java
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Candidate.java
opendaylight/md-sal/sal-clustering-config/src/main/resources/initial/datastore.cfg
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DatastoreContext.java

index fb571f11f84179fcfe49ff78434b76daf012e945..070218e92e46448a74f82fd2416c1458b2f320c5 100644 (file)
@@ -54,6 +54,14 @@ public interface ConfigParams {
      */
     FiniteDuration getElectionTimeOutInterval();
 
+    /**
+     * Returns the number by which a candidate should divide the election timeout it has calculated. This serves
+     * to speed up retries when elections result in a stalemate.
+     *
+     * @return the interval as a FiniteDuration.
+     */
+    long getCandidateElectionTimeoutDivisor();
+
     /**
      * Returns the maximum election time variance. The election is scheduled using both the election timeout
      * and variance.
index 53728043fb8fcdf1a7444f27886eef19bedcb16f..4185754da462651e8c121f5b6d3cf312f5f5ace6 100644 (file)
@@ -66,6 +66,7 @@ public class DefaultConfigParamsImpl implements ConfigParams {
     private int snapshotChunkSize = SNAPSHOT_CHUNK_SIZE;
 
     private long electionTimeoutFactor = 2;
+    private long candidateElectionTimeoutDivisor = 1;
     private String customRaftPolicyImplementationClass;
 
     private PeerAddressResolver peerAddressResolver = NoopPeerAddressResolver.INSTANCE;
@@ -106,6 +107,10 @@ public class DefaultConfigParamsImpl implements ConfigParams {
         electionTimeOutInterval = null;
     }
 
+    public void setCandidateElectionTimeoutDivisor(final long candidateElectionTimeoutDivisor) {
+        this.candidateElectionTimeoutDivisor = candidateElectionTimeoutDivisor;
+    }
+
     public void setTempFileDirectory(final String tempFileDirectory) {
         this.tempFileDirectory = tempFileDirectory;
     }
@@ -133,7 +138,6 @@ public class DefaultConfigParamsImpl implements ConfigParams {
         return snapshotDataThresholdPercentage;
     }
 
-
     @Override
     public FiniteDuration getHeartBeatInterval() {
         return heartBeatInterval;
@@ -148,6 +152,11 @@ public class DefaultConfigParamsImpl implements ConfigParams {
         return electionTimeOutInterval;
     }
 
+    @Override
+    public long getCandidateElectionTimeoutDivisor() {
+        return candidateElectionTimeoutDivisor;
+    }
+
     @Override
     public int getElectionTimeVariance() {
         return ELECTION_TIME_MAX_VARIANCE;
index 7c111d28de818e225eb4b1ab8e10d4f962198c72..afa46892bea33754340f7ef6891f8cdbe646ed61 100644 (file)
@@ -21,6 +21,7 @@ import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply;
 import org.opendaylight.controller.cluster.raft.messages.RaftRPC;
 import org.opendaylight.controller.cluster.raft.messages.RequestVote;
 import org.opendaylight.controller.cluster.raft.messages.RequestVoteReply;
+import scala.concurrent.duration.FiniteDuration;
 
 /**
  * The behavior of a RaftActor when it is in the Candidate raft state.
@@ -123,6 +124,11 @@ public class Candidate extends AbstractRaftActorBehavior {
         return this;
     }
 
+    @Override
+    protected FiniteDuration electionDuration() {
+        return super.electionDuration().$div(context.getConfigParams().getCandidateElectionTimeoutDivisor());
+    }
+
     @Override
     public RaftActorBehavior handleMessage(ActorRef sender, Object message) {
         if (message instanceof ElectionTimeout) {
index 20d28919f9bbae35072d438f108258a6b5f4da63..250a8149ff1e0b77e6f203c275beb473c03d8c2e 100644 (file)
@@ -12,6 +12,11 @@ shard-election-timeout-factor=20
 # The interval at which a shard will send a heart beat message to its remote shard.
 #shard-heartbeat-interval-in-millis=500
 
+# The amount by which to divide election timeout in case of a candidate. This serves as a counter-balance
+# to shard-election-timeout-factor. The default value is 1, i.e. election timeout is the same in all
+# situations.
+#shard-candidate-election-timeout-divisor=1
+
 # The maximum amount of time to wait for a shard to elect a leader before failing an operation (eg transaction create).
 #shard-leader-election-timeout-in-seconds=30
 
index 98afd7f4e02fc76354fd22765e75ed2ba8b98123..1a46e3d4525f10f4bc616153e1f74733eeb81192 100644 (file)
@@ -53,6 +53,7 @@ public class DatastoreContext implements ClientActorConfig {
     public static final FileAkkaConfigurationReader DEFAULT_CONFIGURATION_READER = new FileAkkaConfigurationReader();
     public static final int DEFAULT_SHARD_SNAPSHOT_DATA_THRESHOLD_PERCENTAGE = 12;
     public static final int DEFAULT_SHARD_ELECTION_TIMEOUT_FACTOR = 2;
+    public static final int DEFAULT_SHARD_CANDIDATE_ELECTION_TIMEOUT_DIVISOR = 1;
     public static final int DEFAULT_TX_CREATION_INITIAL_RATE_LIMIT = 100;
     public static final String UNKNOWN_DATA_STORE_TYPE = "unknown";
     public static final int DEFAULT_SHARD_BATCHED_MODIFICATION_COUNT = 1000;
@@ -106,6 +107,7 @@ public class DatastoreContext implements ClientActorConfig {
         setIsolatedLeaderCheckInterval(DEFAULT_ISOLATED_LEADER_CHECK_INTERVAL_IN_MILLIS);
         setSnapshotDataThresholdPercentage(DEFAULT_SHARD_SNAPSHOT_DATA_THRESHOLD_PERCENTAGE);
         setElectionTimeoutFactor(DEFAULT_SHARD_ELECTION_TIMEOUT_FACTOR);
+        setCandidateElectionTimeoutDivisor(DEFAULT_SHARD_CANDIDATE_ELECTION_TIMEOUT_DIVISOR);
         setSyncIndexThreshold(DEFAULT_SYNC_INDEX_THRESHOLD);
         setMaximumMessageSliceSize(DEFAULT_MAX_MESSAGE_SLICE_SIZE);
     }
@@ -142,6 +144,7 @@ public class DatastoreContext implements ClientActorConfig {
         setIsolatedLeaderCheckInterval(other.raftConfig.getIsolatedCheckIntervalInMillis());
         setSnapshotDataThresholdPercentage(other.raftConfig.getSnapshotDataThresholdPercentage());
         setElectionTimeoutFactor(other.raftConfig.getElectionTimeoutFactor());
+        setCandidateElectionTimeoutDivisor(other.raftConfig.getCandidateElectionTimeoutDivisor());
         setCustomRaftPolicyImplementation(other.raftConfig.getCustomRaftPolicyImplementationClass());
         setMaximumMessageSliceSize(other.getMaximumMessageSliceSize());
         setShardSnapshotChunkSize(other.raftConfig.getSnapshotChunkSize());
@@ -268,6 +271,10 @@ public class DatastoreContext implements ClientActorConfig {
         raftConfig.setElectionTimeoutFactor(shardElectionTimeoutFactor);
     }
 
+    private void setCandidateElectionTimeoutDivisor(final long candidateElectionTimeoutDivisor) {
+        raftConfig.setCandidateElectionTimeoutDivisor(candidateElectionTimeoutDivisor);
+    }
+
     private void setCustomRaftPolicyImplementation(final String customRaftPolicyImplementation) {
         raftConfig.setCustomRaftPolicyImplementationClass(customRaftPolicyImplementation);
     }
@@ -473,6 +480,11 @@ public class DatastoreContext implements ClientActorConfig {
             return this;
         }
 
+        public Builder shardCandidateElectionTimeoutDivisor(final long candidateElectionTimeoutDivisor) {
+            datastoreContext.setCandidateElectionTimeoutDivisor(candidateElectionTimeoutDivisor);
+            return this;
+        }
+
         public Builder transactionCreationInitialRateLimit(final long initialRateLimit) {
             datastoreContext.transactionCreationInitialRateLimit = initialRateLimit;
             return this;