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

  1. Honor the interruption: stop the check and exit or reschedule after restart.
  2. If polling in a loop, catch UnavailableException, check Thread.currentThread().isInterrupted(), and break the loop.
  3. 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

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


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/8d080c4f0d022b6b. Report an issue: GitHub.