{"record":{"id":"d4f99d192a1237ad","repo":"apache/pulsar","slug":"interrupted-while-reading-ledgers-at-path-path","errorCode":null,"errorMessage":"Interrupted while reading ledgers at path ${path}","messagePattern":"Interrupted while reading ledgers at path (.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"warning","filePath":"pulsar-metadata/src/main/java/org/apache/pulsar/metadata/bookkeeper/LongHierarchicalLedgerRangeIterator.java","lineNumber":69,"sourceCode":"     * Returns all children with path as a parent.  If path is non-existent,\n     * returns an empty list anyway (after all, there are no children there).\n     * Maps all exceptions (other than NoNode) to IOException in keeping with\n     * LedgerRangeIterator.\n     *\n     * @param path\n     * @return Iterator into set of all children with path as a parent\n     * @throws IOException\n     */\n    List<String> getChildrenAt(String path) throws IOException {\n        try {\n            return store.sync(path).thenCompose(__ -> store.getChildrenFromStore(path))\n                    .get(AbstractMetadataDriver.BLOCKING_CALL_TIMEOUT, TimeUnit.MILLISECONDS);\n        } catch (ExecutionException | TimeoutException e) {\n            log.debug().attr(\"path\", path).log(\"Failed to get children\");\n            throw new IOException(e);\n        } catch (InterruptedException ie) {\n            Thread.currentThread().interrupt();\n            throw new IOException(\"Interrupted while reading ledgers at path \" + path, ie);\n        }\n    }\n\n    /**\n     * Represents the ledger range rooted at a leaf node, returns at most one LedgerRange.\n     */\n    class LeafIterator implements LedgerManager.LedgerRangeIterator {\n        // Null iff iteration is complete\n        LedgerManager.LedgerRange range;\n\n        LeafIterator(String path) throws IOException {\n            List<String> ledgerLeafNodes = getChildrenAt(path);\n            Set<Long> ledgerIds = HierarchicalLedgerUtils.ledgerListToSet(ledgerLeafNodes, ledgerRootPath, path);\n            log.debug().attr(\"hashNode\", path)\n                    .attr(\"ledgers\", ledgerIds)\n                    .log(\"All active ledgers from ZK for hash node\");\n            if (!ledgerIds.isEmpty()) {\n                range = new LedgerManager.LedgerRange(ledgerIds);","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-metadata/src/main/java/org/apache/pulsar/metadata/bookkeeper/LongHierarchicalLedgerRangeIterator.java#L51-L87","documentation":"getChildrenAt() blocks on a CompletableFuture with .get(timeout); if the waiting thread is interrupted, it re-asserts the interrupt flag and wraps the InterruptedException in an IOException with the message 'Interrupted while reading ledgers at path <path>'. This is not a store failure — the caller's thread was interrupted while blocked.","triggerScenarios":"Thread interruption while getChildrenAt(path) is blocked on .get(..., BLOCKING_CALL_TIMEOUT, ...) — typically broker shutdown, task cancellation, or an executor that interrupts workers.","commonSituations":"Graceful shutdown of a broker/bookie mid ledger-range scan; a cancelled scheduled job iterating ledgers; thread pool shutdownNow during long hierarchical ledger enumeration.","solutions":["Let shutdown proceed — this is expected during termination; stop the iteration.","Avoid interrupting worker threads running ledger scans, or run scans in a cancellable task you control.","If seen unexpectedly, audit for code calling interrupt() on threads doing metadata reads.","Preserve and honor the interrupt flag (the library already does Thread.currentThread().interrupt()); do not swallow it in retry loops."],"exampleFix":"// before\ncatch (IOException e) { e.printStackTrace(); /* keep iterating */ }\n// after\ncatch (IOException e) {\n    if (Thread.currentThread().isInterrupted()) {\n        return; // shutdown in progress, stop scanning\n    }\n    throw e;\n}","handlingStrategy":"try-catch","validationCode":"// check interrupt status before starting the scan\nif (Thread.currentThread().isInterrupted()) { return; }","typeGuard":null,"tryCatchPattern":"try { ... } catch (IOException e) {\n  if (Thread.currentThread().isInterrupted()) { /* stop cleanly */ return; }\n  throw e;\n}","preventionTips":["Don't call shutdownNow() on threads doing metadata reads unless aborting","Check Thread.interrupted() in long loops","Make shutdown paths stop scans gracefully","Keep scans short-lived or cancellable"],"tags":["interruption","threading","shutdown","bookkeeper"],"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"}