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 fb571f1..070218e 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 5372804..4185754 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 7c111d2..afa4689 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 20d2891..250a814 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 98afd7f..1a46e3d 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;

©2013 OpenDaylight, A Linux Foundation Collaborative Project. All Rights Reserved.
OpenDaylight is a registered trademark of The OpenDaylight Project, Inc.
Linux Foundation and OpenDaylight are registered trademarks of the Linux Foundation.
Linux is a registered trademark of Linus Torvalds.