You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@artemis.apache.org by "ASF GitHub Bot (Jira)" <ji...@apache.org> on 2026/07/17 14:40:00 UTC
[jira] [Updated] (ARTEMIS-6159) A lock coordinator with an unreachable ZooKeeper hangs broker startup and shutdown indefinitely
[ https://issues.apache.org/jira/browse/ARTEMIS-6159?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
ASF GitHub Bot updated ARTEMIS-6159:
------------------------------------
Labels: pull-request-available (was: )
> A lock coordinator with an unreachable ZooKeeper hangs broker startup and shutdown indefinitely
> ------------------------------------------------------------------------------------------------
>
> Key: ARTEMIS-6159
> URL: https://issues.apache.org/jira/browse/ARTEMIS-6159
> Project: Artemis
> Issue Type: Bug
> Components: Broker
> Affects Versions: 2.51.0, 2.52.0, 2.54.0, 2.53.0, 2.55.0
> Reporter: Michael Lukyanov
> Priority: Major
> Labels: pull-request-available
> Time Spent: 10m
> Remaining Estimate: 0h
>
> h3. Summary
> When a broker is configured with a ZooKeeper-based lock coordinator
> ({{CuratorDistributedLockManager}}) and ZooKeeper is unreachable, the lock
> coordinator's periodic task blocks forever trying to connect. This occupies
> the coordinator's ordered-executor thread permanently, and broker
> stop/restart hangs indefinitely as a result. The broker becomes
> unmanageable and the log fills with an endless ZooKeeper reconnect loop.
> Also reproduces when the lock coordinator is defined but not referenced by
> any acceptor or broker-connection - every configured {{<lock-coordinator>}}
> gets started unconditionally, so an unused one can hang the broker just as
> well.
> h3. Root cause
> Confirmed against {{main}} (2.56.0-SNAPSHOT), present since 2.51.0.
> {{LockCoordinator.run()}} starts the manager with the unbounded overload,
> {{lockManager.start()}} (LockCoordinator.java:316). {{CuratorDistributedLockManager.start()}}
> delegates to {{start(-1, null)}} (line 260), which calls
> {{client.blockUntilConnected((int) timeout, unit)}} with {{unit == null}}
> (line 244). With a null {{TimeUnit}}, Curator's {{blockUntilConnected}}
> waits indefinitely - even though Curator itself keeps retrying in the
> background, hence the endless "Opening socket connection.../SASL
> configuration failed/Authentication failed" loop in the logs.
> {{LockCoordinator}} extends {{ActiveMQScheduledComponent}} and its scheduled
> task runs on the coordinator's own ordered executor ({{startLockCoordinators()}}
> does {{new LockCoordinator(scheduledPool, executorFactory.getExecutor(), ...)}}).
> Once {{run()}} is stuck in {{blockUntilConnected}}, that executor thread is
> gone for good.
> Then on stop/restart, {{LockCoordinator.stop()}} (line 168) queues its
> cleanup on the same ordered executor and blocks on {{simpleFuture.get()}}
> with no timeout (line 197). That cleanup task never gets to run, so
> {{ActiveMQServerImpl.stopLockCoordinators()}} -> {{LockCoordinator.stop()}}
> never returns and the broker can't be stopped or restarted.
> Separately, {{ActiveMQServerImpl.startLockCoordinators()}} (lines 824-844)
> starts every configured coordinator regardless of whether anything
> references it via {{lock-coordinator="..."}}. Not the cause of the hang by
> itself, but it means a coordinator nobody uses can still take the broker
> down.
> h3. Steps to reproduce
> Configure a coordinator pointing at an address nothing is listening on:
> {code:xml}
> <lock-coordinators>
> <lock-coordinator name="zk">
> <class-name>org.apache.activemq.artemis.lockmanager.zookeeper.CuratorDistributedLockManager</class-name>
> <properties>
> <property key="connect-string" value="localhost:2181"/>
> <property key="namespace" value="test"/>
> </properties>
> </lock-coordinator>
> </lock-coordinators>
> {code}
> Don't start ZooKeeper, start the broker, then try to stop or restart it. It
> doesn't come back. Assigning the coordinator to an acceptor isn't necessary
> to trigger this - an unreferenced one hangs the broker just the same.
> h3. Fix
> I wrote two tests to pin this down before touching anything: one in
> artemis-server with a fake {{DistributedLockManager}} whose {{start()}}
> just blocks (same shape as Curator against a dead ensemble), asserting
> {{LockCoordinator.stop()}} returns; one in integration-tests with a real
> {{CuratorDistributedLockManager}} pointed at a closed port and the
> coordinator left unassigned, asserting {{server.stop()}} returns. Both hang
> and fail on {{main}} as-is (~31s and ~72s respectively before the assertion
> gives up), and both pass once the fix below is applied (under a second, and
> a couple seconds).
> The fix is just bounding the start call in {{LockCoordinator.run()}}:
> {code:java}
> if (!lockManager.isStarted()) {
> if (!lockManager.start(getPeriod(), getTimeUnit())) {
> logger.debug("Not able to start the lock manager for {}, lockID={}", name, lockID);
> return;
> }
> }
> {code}
> {{getPeriod()}}/{{getTimeUnit()}} are just the coordinator's own
> {{checkPeriod}}, inherited from {{ActiveMQScheduledComponent}} - so the
> connect attempt is bounded to one scheduling tick, and on timeout it's
> retried on the next one at the same cadence it already runs at. No new
> config knob needed.
> Turns out that's all that's needed - I didn't touch {{stop()}}. Once
> {{run()}} can't block forever, the cleanup task queued by {{stop()}} gets
> its turn on the executor within one {{checkPeriod}}, so
> {{simpleFuture.get()}} returns on its own without a separate timeout. Both
> tests above confirm this.
> Left the unreferenced-coordinator behavior alone since it's not needed to
> fix the hang - up to whoever picks this up whether it's worth addressing
> separately.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@artemis.apache.org
For additional commands, e-mail: issues-help@artemis.apache.org