{"record":{"id":"8d080c4f0d022b6b","repo":"apache/pulsar","slug":"interrupted-while-contacting-zookeeper","errorCode":null,"errorMessage":"Interrupted while contacting zookeeper","messagePattern":"Interrupted while contacting zookeeper","errorType":"exception","errorClass":"ReplicationException.UnavailableException","httpStatus":null,"severity":"warning","filePath":"pulsar-metadata/src/main/java/org/apache/pulsar/metadata/bookkeeper/PulsarLedgerUnderreplicationManager.java","lineNumber":763,"sourceCode":"            throw new ReplicationException.UnavailableException(\n                    \"Interrupted while resuming auto ledger re-replication\", ie);\n        }\n    }\n\n    @Override\n    public boolean isLedgerReplicationEnabled()\n            throws ReplicationException.UnavailableException {\n        log.debug(\"isLedgerReplicationEnabled()\");\n        try {\n            return !store.exists(replicationDisablePath)\n                    .get(BLOCKING_CALL_TIMEOUT, MILLISECONDS);\n        } catch (ExecutionException | TimeoutException ee) {\n            log.error().exception(ee).log(\"Error while checking the state of ledger re-replication\");\n            throw new ReplicationException.UnavailableException(\n                    \"Error contacting zookeeper\", ee);\n        } catch (InterruptedException ie) {\n            Thread.currentThread().interrupt();\n            throw new ReplicationException.UnavailableException(\n                    \"Interrupted while contacting zookeeper\", ie);\n        }\n    }\n\n    @Override\n    public void notifyLedgerReplicationEnabled(final BookkeeperInternalCallbacks.GenericCallback<Void> cb)\n            throws ReplicationException.UnavailableException {\n        log.debug(\"notifyLedgerReplicationEnabled()\");\n        synchronized (replicationEnabledCallbacks) {\n            replicationEnabledCallbacks.add(cb);\n        }\n        try {\n            if (!store.exists(replicationDisablePath)\n                    .get(BLOCKING_CALL_TIMEOUT, MILLISECONDS)) {\n                log.info(\"LedgerReplication is enabled externally through metadata store,\"\n                        + \" since DISABLE_NODE node is deleted\");\n                cb.operationComplete(0, null);\n                return;","sourceCodeStart":745,"sourceCodeEnd":781,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-metadata/src/main/java/org/apache/pulsar/metadata/bookkeeper/PulsarLedgerUnderreplicationManager.java#L745-L781","documentation":"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.","triggerScenarios":"Thread interruption during the blocking store.exists(replicationDisablePath).get(BLOCKING_CALL_TIMEOUT, MILLISECONDS) call.","commonSituations":"Service shutdown interrupts a bookkeeper auditor thread mid-check; a task framework cancels long-running polling threads.","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."],"exampleFix":"// before\nwhile (running) { state = urManager.isLedgerReplicationEnabled(); }\n// after\nwhile (running) {\n    try {\n        state = urManager.isLedgerReplicationEnabled();\n    } catch (ReplicationException.UnavailableException e) {\n        if (Thread.currentThread().isInterrupted()) break;\n        sleep(retryInterval);\n    }\n}","handlingStrategy":"try-catch","validationCode":"if (Thread.interrupted()) {\n    return; // don't even start the check\n}","typeGuard":"boolean isInterruption(ReplicationException.UnavailableException e) {\n    return e.getCause() instanceof InterruptedException;\n}","tryCatchPattern":"try {\n    state = urManager.isLedgerReplicationEnabled();\n} catch (ReplicationException.UnavailableException e) {\n    if (e.getCause() instanceof InterruptedException) {\n        Thread.currentThread().interrupt(); // already restored, but idempotent signal\n        break; // exit polling loop\n    }\n}","preventionTips":["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."],"tags":["interruption","threading"],"backgroundTag":"thread-interrupted","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}