This is an automated email from the ASF dual-hosted git repository.

szetszwo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ratis.git


The following commit(s) were added to refs/heads/master by this push:
     new e49687aba RATIS-1638. Separate first election timeout (#713)
e49687aba is described below

commit e49687aba20cad70c576900f1be59bb8fcd55d10
Author: William Song <[email protected]>
AuthorDate: Fri Aug 19 23:18:29 2022 +0800

    RATIS-1638. Separate first election timeout (#713)
---
 .../main/java/org/apache/ratis/conf/ConfUtils.java | 14 +++++++-
 .../apache/ratis/server/RaftServerConfigKeys.java  | 39 +++++++++++++++++++---
 .../apache/ratis/server/impl/RaftServerImpl.java   | 17 ++++++++++
 .../org/apache/ratis/server/impl/ServerState.java  |  1 +
 4 files changed, 66 insertions(+), 5 deletions(-)

diff --git a/ratis-common/src/main/java/org/apache/ratis/conf/ConfUtils.java 
b/ratis-common/src/main/java/org/apache/ratis/conf/ConfUtils.java
index 681c0dc67..629d5a389 100644
--- a/ratis-common/src/main/java/org/apache/ratis/conf/ConfUtils.java
+++ b/ratis-common/src/main/java/org/apache/ratis/conf/ConfUtils.java
@@ -214,6 +214,17 @@ public interface ConfUtils {
     return value;
   }
 
+  @SafeVarargs
+  static TimeDuration getTimeDuration(
+        BiFunction<String, TimeDuration, TimeDuration> getter,
+        String key, TimeDuration defaultValue, String fallbackKey, 
TimeDuration fallbackValue,
+        Consumer<String> logger, BiConsumer<String, TimeDuration>... 
assertions) {
+    final TimeDuration value = get(getter, key, defaultValue, fallbackKey, 
fallbackValue, logger, assertions);
+    requireNonNegativeTimeDuration().accept(key, value);
+    return value;
+  }
+
+
   static TlsConf getTlsConf(
       Function<String, TlsConf> tlsConfGetter,
       String key, Consumer<String> logger) {
@@ -236,10 +247,11 @@ public interface ConfUtils {
     T value = get(getter, key, defaultValue, null, assertions);
     if (value != defaultValue) {
       logGet(key, value, defaultValue, logger);
+      return value;
     } else {
       logFallback(key, fallbackKey, fallbackValue, logger);
+      return fallbackValue;
     }
-    return value;
   }
 
   static InetSocketAddress getInetSocketAddress(
diff --git 
a/ratis-server-api/src/main/java/org/apache/ratis/server/RaftServerConfigKeys.java
 
b/ratis-server-api/src/main/java/org/apache/ratis/server/RaftServerConfigKeys.java
index 6d0c5d41b..e24631378 100644
--- 
a/ratis-server-api/src/main/java/org/apache/ratis/server/RaftServerConfigKeys.java
+++ 
b/ratis-server-api/src/main/java/org/apache/ratis/server/RaftServerConfigKeys.java
@@ -619,9 +619,12 @@ public interface RaftServerConfigKeys {
 
     String TIMEOUT_MIN_KEY = PREFIX + ".timeout.min";
     TimeDuration TIMEOUT_MIN_DEFAULT = TimeDuration.valueOf(150, 
TimeUnit.MILLISECONDS);
-    static TimeDuration timeoutMin(RaftProperties properties) {
+    static TimeDuration timeoutMin(RaftProperties properties, Consumer<String> 
logger) {
       return 
getTimeDuration(properties.getTimeDuration(TIMEOUT_MIN_DEFAULT.getUnit()),
-          TIMEOUT_MIN_KEY, TIMEOUT_MIN_DEFAULT, getDefaultLog());
+          TIMEOUT_MIN_KEY, TIMEOUT_MIN_DEFAULT, logger);
+    }
+    static TimeDuration timeoutMin(RaftProperties properties) {
+      return timeoutMin(properties, getDefaultLog());
     }
     static void setTimeoutMin(RaftProperties properties, TimeDuration 
minDuration) {
       setTimeDuration(properties::setTimeDuration, TIMEOUT_MIN_KEY, 
minDuration);
@@ -629,14 +632,42 @@ public interface RaftServerConfigKeys {
 
     String TIMEOUT_MAX_KEY = PREFIX + ".timeout.max";
     TimeDuration TIMEOUT_MAX_DEFAULT = TimeDuration.valueOf(300, 
TimeUnit.MILLISECONDS);
-    static TimeDuration timeoutMax(RaftProperties properties) {
+    static TimeDuration timeoutMax(RaftProperties properties, Consumer<String> 
logger) {
       return 
getTimeDuration(properties.getTimeDuration(TIMEOUT_MAX_DEFAULT.getUnit()),
-          TIMEOUT_MAX_KEY, TIMEOUT_MAX_DEFAULT, getDefaultLog());
+          TIMEOUT_MAX_KEY, TIMEOUT_MAX_DEFAULT, logger);
+    }
+    static TimeDuration timeoutMax(RaftProperties properties) {
+      return timeoutMax(properties, getDefaultLog());
     }
     static void setTimeoutMax(RaftProperties properties, TimeDuration 
maxDuration) {
       setTimeDuration(properties::setTimeDuration, TIMEOUT_MAX_KEY, 
maxDuration);
     }
 
+    /** separate first timeout so that the startup unavailable time can be 
reduced */
+    String FIRST_ELECTION_TIMEOUT_MIN_KEY = PREFIX + 
".first-election.timeout.min";
+    TimeDuration FIRST_ELECTION_TIMEOUT_MIN_DEFAULT = null;
+    static TimeDuration firstElectionTimeoutMin(RaftProperties properties) {
+      final TimeDuration fallbackFirstElectionTimeoutMin = 
Rpc.timeoutMin(properties, null);
+      return 
getTimeDuration(properties.getTimeDuration(fallbackFirstElectionTimeoutMin.getUnit()),
+          FIRST_ELECTION_TIMEOUT_MIN_KEY, FIRST_ELECTION_TIMEOUT_MIN_DEFAULT,
+          Rpc.TIMEOUT_MIN_KEY, fallbackFirstElectionTimeoutMin, 
getDefaultLog());
+    }
+    static void setFirstElectionTimeoutMin(RaftProperties properties, 
TimeDuration firstMinDuration) {
+      setTimeDuration(properties::setTimeDuration, 
FIRST_ELECTION_TIMEOUT_MIN_KEY, firstMinDuration);
+    }
+
+    String FIRST_ELECTION_TIMEOUT_MAX_KEY = PREFIX + 
".first-election.timeout.max";
+    TimeDuration FIRST_ELECTION_TIMEOUT_MAX_DEFAULT = null;
+    static TimeDuration firstElectionTimeoutMax(RaftProperties properties) {
+      final TimeDuration fallbackFirstElectionTimeoutMax = 
Rpc.timeoutMax(properties, null);
+      return 
getTimeDuration(properties.getTimeDuration(fallbackFirstElectionTimeoutMax.getUnit()),
+          FIRST_ELECTION_TIMEOUT_MAX_KEY, FIRST_ELECTION_TIMEOUT_MAX_DEFAULT,
+          Rpc.TIMEOUT_MAX_KEY, fallbackFirstElectionTimeoutMax, 
getDefaultLog());
+    }
+    static void setFirstElectionTimeoutMax(RaftProperties properties, 
TimeDuration firstMaxDuration) {
+      setTimeDuration(properties::setTimeDuration, 
FIRST_ELECTION_TIMEOUT_MAX_KEY, firstMaxDuration);
+    }
+
     String REQUEST_TIMEOUT_KEY = PREFIX + ".request.timeout";
     TimeDuration REQUEST_TIMEOUT_DEFAULT = TimeDuration.valueOf(3000, 
TimeUnit.MILLISECONDS);
     static TimeDuration requestTimeout(RaftProperties properties) {
diff --git 
a/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java 
b/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java
index 4c798d9d5..2ee191c56 100644
--- 
a/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java
+++ 
b/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java
@@ -187,6 +187,8 @@ class RaftServerImpl implements RaftServer.Division,
   private final ExecutorService serverExecutor;
   private final ExecutorService clientExecutor;
 
+  private final AtomicBoolean firstElectionSinceStartup = new 
AtomicBoolean(true);
+
   RaftServerImpl(RaftGroup group, StateMachine stateMachine, RaftServerProxy 
proxy) throws IOException {
     final RaftPeerId id = proxy.getId();
     LOG.info("{}: new RaftServerImpl for {} with {}", id, group, stateMachine);
@@ -245,11 +247,22 @@ class RaftServerImpl implements RaftServer.Division,
   }
 
   TimeDuration getRandomElectionTimeout() {
+    if (firstElectionSinceStartup.get()) {
+      return getFirstRandomElectionTimeout();
+    }
     final int min = properties().minRpcTimeoutMs();
     final int millis = min + 
ThreadLocalRandom.current().nextInt(properties().maxRpcTimeoutMs() - min + 1);
     return TimeDuration.valueOf(millis, TimeUnit.MILLISECONDS);
   }
 
+  private TimeDuration getFirstRandomElectionTimeout() {
+    final RaftProperties properties = proxy.getProperties();
+    final int min = 
RaftServerConfigKeys.Rpc.firstElectionTimeoutMin(properties).toIntExact(TimeUnit.MILLISECONDS);
+    final int max = 
RaftServerConfigKeys.Rpc.firstElectionTimeoutMax(properties).toIntExact(TimeUnit.MILLISECONDS);
+    final int mills = min + ThreadLocalRandom.current().nextInt(max - min + 1);
+    return TimeDuration.valueOf(mills, TimeUnit.MILLISECONDS);
+  }
+
   TimeDuration getLeaderStepDownWaitTime() {
     return leaderStepDownWaitTime;
   }
@@ -1725,4 +1738,8 @@ class RaftServerImpl implements RaftServer.Division,
           .collect(Collectors.toList());
     }
   }
+
+  void onGroupLeaderElected() {
+    this.firstElectionSinceStartup.set(false);
+  }
 }
diff --git 
a/ratis-server/src/main/java/org/apache/ratis/server/impl/ServerState.java 
b/ratis-server/src/main/java/org/apache/ratis/server/impl/ServerState.java
index 34e9cb92c..1ee2cab5e 100644
--- a/ratis-server/src/main/java/org/apache/ratis/server/impl/ServerState.java
+++ b/ratis-server/src/main/java/org/apache/ratis/server/impl/ServerState.java
@@ -315,6 +315,7 @@ class ServerState implements Closeable {
       leaderId = newLeaderId;
       if (leaderId != null) {
         server.finishTransferLeadership();
+        server.onGroupLeaderElected();
       }
     }
   }

Reply via email to