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]
