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

rmaucher pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit ae752c0133899a757e8ea8315b97d3f514d69cb2
Author: opencode <[email protected]>
AuthorDate: Fri Oct 9 14:26:39 2026 +0200

    Make NonBlockingCoordinator coordination wait timeout configurable
    
    startElection() blocks the calling thread while a non-leader node
    waits for the election request from the higher priority member. For
    membership events the caller is the channel utility executor, by
    default a single-threaded scheduled executor that also runs the
    channel heartbeat and all other membership callbacks. The wait used a
    hard-coded, final timeout of 15 seconds, so on every membership change
    where the expected coordination message did not arrive - typically the
    coordinator-down or partition case - failover and membership
    convergence were stalled for up to 15 seconds per queued election.
    
    Make the timeout configurable via a new waitForCoordMsgTimeout
    property, keeping the 15 second default. The value is bounded to at
    most Integer.MAX_VALUE milliseconds since it is also used, after a
    narrowing cast, as the TCP connect timeout for member liveness checks
    during a merge; the javadoc now documents that dual use.
---
 .../group/interceptors/LocalStrings.properties     |  2 +
 .../group/interceptors/NonBlockingCoordinator.java | 54 ++++++++++++++++++++--
 2 files changed, 51 insertions(+), 5 deletions(-)

diff --git 
a/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties 
b/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties
index 03dc547b05..63584f6e4c 100644
--- a/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties
+++ b/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties
@@ -78,6 +78,8 @@ nonBlockingCoordinator.report=CoordinationEvent[type={0}\n\
 \tMembers: {5}\n\
 \tInfo: {6}\n\
 ]
+nonBlockingCoordinator.waitForCoordMsgTimeout.tooLarge=Coordination message 
wait timeout must be no more than [{0}] milliseconds
+nonBlockingCoordinator.waitForCoordMsgTimeout.tooSmall=Coordination message 
wait timeout must be at least 1 millisecond
 
 orderInterceptor.messageAdded.sameCounter=Message added has the same counter, 
synchronization bug. Disable the order interceptor
 
diff --git 
a/java/org/apache/catalina/tribes/group/interceptors/NonBlockingCoordinator.java
 
b/java/org/apache/catalina/tribes/group/interceptors/NonBlockingCoordinator.java
index 2a19fb97cc..137a5d2817 100644
--- 
a/java/org/apache/catalina/tribes/group/interceptors/NonBlockingCoordinator.java
+++ 
b/java/org/apache/catalina/tribes/group/interceptors/NonBlockingCoordinator.java
@@ -52,8 +52,9 @@ import org.apache.juli.logging.LogFactory;
  * </p>
  * <p>
  * This algorithm is non blocking meaning it allows for transactions while the 
coordination phase is going on. Note
- * that {@link #startElection(boolean)} itself blocks its caller for up to 15 
seconds while waiting for an election
- * request from a higher priority member.
+ * that {@link #startElection(boolean)} itself blocks its caller for up to
+ * {@link #getWaitForCoordMsgTimeout()} milliseconds while waiting for an 
election request from a higher priority
+ * member.
  * </p>
  * <p>
  * This implementation is based on a home brewed algorithm that uses the 
AbsoluteOrder of a membership to pass a token
@@ -151,9 +152,15 @@ public class NonBlockingCoordinator extends 
ChannelInterceptorBase {
             50, 111, 103, 65, 3, -77, 51, -35, 0, 119, 117, 9, -26, 119, 50, 
-75, -105, -102, 36, 79, 37, -68, -84,
             -123, 15, -22, -109, 106, -55 };
     /**
-     * Time to wait for coordination timeout
+     * Time to wait for coordination timeout. A node that is not the leader in 
its view
+     * waits up to this duration for a coordination message when an election 
is started.
+     * The wait blocks the calling thread, which for membership events is the 
channel
+     * utility executor thread that also runs the channel heartbeat. Lowering 
this value
+     * reduces the time that a single membership event can delay heartbeats 
and other
+     * membership processing. The same value is also used as the TCP connect 
timeout
+     * when verifying the liveness of members during a merge.
      */
-    protected final long waitForCoordMsgTimeout = 15000;
+    protected long waitForCoordMsgTimeout = 15000;
     /**
      * Our current view
      */
@@ -203,6 +210,42 @@ public class NonBlockingCoordinator extends 
ChannelInterceptorBase {
         super();
     }
 
+    /**
+     * Returns the time in milliseconds that a non-leader node waits for a 
coordination
+     * message when an election is started. The same value is also used as the 
TCP
+     * connect timeout when verifying the liveness of members during a merge.
+     *
+     * @return the coordination message wait timeout in milliseconds
+     */
+    public long getWaitForCoordMsgTimeout() {
+        return waitForCoordMsgTimeout;
+    }
+
+    /**
+     * Sets the time in milliseconds that a non-leader node waits for a 
coordination
+     * message when an election is started. The same value is also used as the 
TCP
+     * connect timeout when verifying the liveness of members during a merge. 
Since the
+     * wait blocks the calling thread, which for membership events is the 
single
+     * threaded channel utility executor that also runs the channel heartbeat, 
a lower
+     * value limits the delay that a single membership event can impose on 
other channel
+     * maintenance work.
+     *
+     * @param waitForCoordMsgTimeout the coordination message wait timeout in
+     *                                   milliseconds, at least one and no 
more than
+     *                                   {@link Integer#MAX_VALUE}
+     */
+    public void setWaitForCoordMsgTimeout(long waitForCoordMsgTimeout) {
+        if (waitForCoordMsgTimeout < 1) {
+            throw new IllegalArgumentException(
+                    
sm.getString("nonBlockingCoordinator.waitForCoordMsgTimeout.tooSmall"));
+        }
+        if (waitForCoordMsgTimeout > Integer.MAX_VALUE) {
+            throw new 
IllegalArgumentException(sm.getString("nonBlockingCoordinator.waitForCoordMsgTimeout.tooLarge",
+                    Integer.toString(Integer.MAX_VALUE)));
+        }
+        this.waitForCoordMsgTimeout = waitForCoordMsgTimeout;
+    }
+
     // 
============================================================================================================
     // COORDINATION HANDLING
     // 
============================================================================================================
@@ -356,7 +399,8 @@ public class NonBlockingCoordinator extends 
ChannelInterceptorBase {
     }
 
     /**
-     * Checks if a member is alive using the default timeout.
+     * Checks if a member is alive using the coordination message wait timeout
+     * ({@link #getWaitForCoordMsgTimeout()}) as the connection timeout.
      *
      * @param mbr The member to check
      * @return true if the member is alive


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to