{"record":{"id":"8cb5d955b94a525c","repo":"apache/pulsar","slug":"interrupted-while-connecting-metadata-store","errorCode":null,"errorMessage":"Interrupted while connecting metadata store","messagePattern":"Interrupted while connecting metadata store","errorType":"exception","errorClass":"ReplicationException.UnavailableException","httpStatus":null,"severity":"warning","filePath":"pulsar-metadata/src/main/java/org/apache/pulsar/metadata/bookkeeper/PulsarLedgerUnderreplicationManager.java","lineNumber":319,"sourceCode":"            }\n\n            byte[] data = optRes.get().getValue();\n\n            UnderreplicatedLedgerFormat underreplicatedLedgerFormat = new UnderreplicatedLedgerFormat();\n\n            underreplicatedLedgerFormat.parseFromTextFormat(data);\n            PulsarUnderreplicatedLedger underreplicatedLedger = new PulsarUnderreplicatedLedger(ledgerId);\n            List<String> replicaList = underreplicatedLedgerFormat.getReplicasList();\n            long ctime = (underreplicatedLedgerFormat.hasCtime() ? underreplicatedLedgerFormat.getCtime()\n                    : UnderreplicatedLedger.UNASSIGNED_CTIME);\n            underreplicatedLedger.setCtime(ctime);\n            underreplicatedLedger.setReplicaList(replicaList);\n            return underreplicatedLedger;\n        } catch (ExecutionException | TimeoutException ee) {\n            throw new ReplicationException.UnavailableException(\"Error contacting with metadata store\", ee);\n        } catch (InterruptedException ie) {\n            Thread.currentThread().interrupt();\n            throw new ReplicationException.UnavailableException(\"Interrupted while connecting metadata store\", ie);\n        } catch (RuntimeException pe) {\n            throw new ReplicationException.UnavailableException(\"Error parsing proto message\", pe);\n        }\n    }\n\n    @Override\n    public CompletableFuture<Void> markLedgerUnderreplicatedAsync(long ledgerId, Collection<String> missingReplicas) {\n        log.debug().attr(\"ledgerId\", ledgerId).attr(\"missingReplicas\", missingReplicas)\n                .log(\"markLedgerUnderreplicated\");\n        final String path = getUrLedgerPath(ledgerId);\n        final CompletableFuture<Void> createFuture = new CompletableFuture<>();\n        tryMarkLedgerUnderreplicatedAsync(path, missingReplicas, createFuture);\n        return createFuture;\n    }\n\n    private void tryMarkLedgerUnderreplicatedAsync(final String path,\n                                                   final Collection<String> missingReplicas,\n                                                   final CompletableFuture<Void> finalFuture) {","sourceCodeStart":301,"sourceCodeEnd":337,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-metadata/src/main/java/org/apache/pulsar/metadata/bookkeeper/PulsarLedgerUnderreplicationManager.java#L301-L337","documentation":"Thrown by getLedgerUnreplicationInfo when the thread blocked waiting on store.get(path).get(...) is interrupted. The code re-asserts the interrupt flag (Thread.currentThread().interrupt()) before wrapping the InterruptedException in ReplicationException.UnavailableException so callers and the executor keep correct interruption semantics. It means the read of the under-replicated ledger state was cancelled rather than failed.","triggerScenarios":"Calling getLedgerUnreplicationInfo from a thread that gets interrupted while blocked on the metadata store future — e.g. broker/Auditor shutdown, executor.shutdownNow(), or another component cancelling the worker thread mid blocking get.","commonSituations":"Graceful shutdown of the Pulsar broker's Auditor thread; a scheduled task cancelled on timeout by an outer scheduler; test harnesses interrupting leaked blocking threads; thread pool termination during redeploy.","solutions":["Treat it as expected cancellation: let the thread exit and do not swallow or clear the interrupt flag.","If it happens unexpectedly, audit which component interrupts the Auditor thread (shutdown hooks, schedulers) and ensure reads are not started on threads about to be cancelled.","If shutdown interrupts are too aggressive, configure orderly shutdown so in-flight replication checks complete before executor termination.","Retry the ledger check after restart; the under-replicated state is persistent in the metadata store."],"exampleFix":"// before\ntry {\n    UnderreplicatedLedger l = urManager.getLedgerUnreplicationInfo(ledgerId);\n} catch (ReplicationException.UnavailableException e) {\n    log.error(\"failed\", e); // ignores interrupt state\n}\n// after\ntry {\n    UnderreplicatedLedger l = urManager.getLedgerUnreplicationInfo(ledgerId);\n} catch (ReplicationException.UnavailableException e) {\n    if (Thread.currentThread().isInterrupted()) {\n        return; // shutdown in progress, stop the loop\n    }\n    retryWithBackoff(...);\n}","handlingStrategy":"try-catch","validationCode":"// skip the call if the thread is already interrupted\nif (Thread.currentThread().isInterrupted()) {\n    return; // don't start blocking store reads on a cancelled thread\n}","typeGuard":"static boolean wasInterrupted(ReplicationException.UnavailableException e) {\n    return e.getCause() instanceof InterruptedException;\n}","tryCatchPattern":"try {\n    return urManager.getLedgerUnreplicationInfo(ledgerId);\n} catch (ReplicationException.UnavailableException e) {\n    if (Thread.currentThread().isInterrupted()) {\n        return null; // shutdown path: exit promptly, don't retry\n    }\n    throw e;\n}","preventionTips":["Ensure executors shut down tasks gracefully before shutdownNow()","Never swallow InterruptedException without restoring the interrupt flag","Avoid running blocking UR reads on threads subject to aggressive cancellation","Check Thread.currentThread().isInterrupted() in long audit loops"],"tags":["interrupt","shutdown","metadata-store","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"}