apache/pulsar · warning · ReplicationException.UnavailableException
Interrupted while contacting zookeeper
Error message
Interrupted while contacting zookeeper
What it means
Thrown by isLedgerReplicationEnabled() when the waiting thread is interrupted while blocked on the exists() future. The interrupt flag is re-set before throwing ReplicationException.UnavailableException so callers can still observe the interruption.
Source
Thrown at pulsar-metadata/src/main/java/org/apache/pulsar/metadata/bookkeeper/PulsarLedgerUnderreplicationManager.java:763
throw new ReplicationException.UnavailableException(
"Interrupted while resuming auto ledger re-replication", ie);
}
}
@Override
public boolean isLedgerReplicationEnabled()
throws ReplicationException.UnavailableException {
log.debug("isLedgerReplicationEnabled()");
try {
return !store.exists(replicationDisablePath)
.get(BLOCKING_CALL_TIMEOUT, MILLISECONDS);
} catch (ExecutionException | TimeoutException ee) {
log.error().exception(ee).log("Error while checking the state of ledger re-replication");
throw new ReplicationException.UnavailableException(
"Error contacting zookeeper", ee);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new ReplicationException.UnavailableException(
"Interrupted while contacting zookeeper", ie);
}
}
@Override
public void notifyLedgerReplicationEnabled(final BookkeeperInternalCallbacks.GenericCallback<Void> cb)
throws ReplicationException.UnavailableException {
log.debug("notifyLedgerReplicationEnabled()");
synchronized (replicationEnabledCallbacks) {
replicationEnabledCallbacks.add(cb);
}
try {
if (!store.exists(replicationDisablePath)
.get(BLOCKING_CALL_TIMEOUT, MILLISECONDS)) {
log.info("LedgerReplication is enabled externally through metadata store,"
+ " since DISABLE_NODE node is deleted");
cb.operationComplete(0, null);
return;View on GitHub (pinned to 820761864e)
Solutions
- Honor the interruption: stop the check and exit or reschedule after restart.
- If polling in a loop, catch UnavailableException, check Thread.currentThread().isInterrupted(), and break the loop.
- Ensure only intended lifecycle owners interrupt these threads.
Example fix
// before
while (running) { state = urManager.isLedgerReplicationEnabled(); }
// after
while (running) {
try {
state = urManager.isLedgerReplicationEnabled();
} catch (ReplicationException.UnavailableException e) {
if (Thread.currentThread().isInterrupted()) break;
sleep(retryInterval);
}
} Defensive patterns
Strategy: try-catch
Validate before calling
if (Thread.interrupted()) {
return; // don't even start the check
} Type guard
boolean isInterruption(ReplicationException.UnavailableException e) {
return e.getCause() instanceof InterruptedException;
} Try / catch
try {
state = urManager.isLedgerReplicationEnabled();
} catch (ReplicationException.UnavailableException e) {
if (e.getCause() instanceof InterruptedException) {
Thread.currentThread().interrupt(); // already restored, but idempotent signal
break; // exit polling loop
}
} Prevention
- Structure polling loops to stop on interruption instead of swallowing it.
- Use dedicated executor lifecycles so cancellation is intentional and observed.
- Avoid interrupting threads that are mid metadata-store call except at shutdown.
When it happens
Trigger: Thread interruption during the blocking store.exists(replicationDisablePath).get(BLOCKING_CALL_TIMEOUT, MILLISECONDS) call.
Common situations: Service shutdown interrupts a bookkeeper auditor thread mid-check; a task framework cancels long-running polling threads.
Related errors
- Interrupted at fetching schema info for <SchemaUtils.getStri
- Interrupted while reading ledgers at path ${path}
- RuntimeException
- IOException
- Interrupted while contacting metadata store
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/8d080c4f0d022b6b.
Report an issue: GitHub.