{"record":{"id":"bf7070f181fa3b51","repo":"apache/pulsar","slug":"ioexception-bf7070","errorCode":null,"errorMessage":"IOException","messagePattern":"IOException","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"warning","filePath":"pulsar-metadata/src/main/java/org/apache/pulsar/metadata/bookkeeper/PulsarLedgerManager.java","lineNumber":367,"sourceCode":"            }\n        }, context, successRc, failureRc);\n    }\n\n    @Override\n    public LedgerRangeIterator getLedgerRanges(long ledgerId) {\n        LedgerRangeIterator iteratorA = new LegacyHierarchicalLedgerRangeIterator(store, ledgerRootPath);\n        LedgerRangeIterator iteratorB = new LongHierarchicalLedgerRangeIterator(store, ledgerRootPath);\n        return new CombinedLedgerRangeIterator(iteratorA, iteratorB);\n    }\n\n    @Override\n    public void close() throws IOException {\n        scheduler.shutdownNow();\n        try {\n            scheduler.awaitTermination(10, TimeUnit.SECONDS);\n        } catch (InterruptedException ie) {\n            Thread.currentThread().interrupt();\n            throw new IOException(ie);\n        }\n    }\n\n    public String getLedgerPath(long ledgerId) {\n        return this.ledgerRootPath + StringUtils.getHybridHierarchicalLedgerPath(ledgerId);\n    }\n\n    private long getLedgerId(String ledgerPath) throws IOException {\n        if (!ledgerPath.startsWith(ledgerRootPath)) {\n            throw new IOException(\"it is not a valid hashed path name : \" + ledgerPath);\n        }\n        String hierarchicalPath = ledgerPath.substring(ledgerRootPath.length() + 1);\n        return StringUtils.stringToLongHierarchicalLedgerId(hierarchicalPath);\n    }\n\n\n    /**\n     * ReadLedgerMetadataTask class.","sourceCodeStart":349,"sourceCodeEnd":385,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-metadata/src/main/java/org/apache/pulsar/metadata/bookkeeper/PulsarLedgerManager.java#L349-L385","documentation":"PulsarLedgerManager.close() shuts down its internal scheduled executor and waits up to 10 seconds for termination. If the waiting thread is interrupted, the InterruptedException is converted to an IOException (after restoring the interrupt flag), signaling that close could not confirm a clean shutdown of background tasks.","triggerScenarios":"Calling close() on the PulsarLedgerManager while the current thread is interrupted — commonly during BookKeeper client shutdown, application stop, or executor shutdownNow() cascading an interrupt.","commonSituations":"Graceful broker shutdown racing ledger-manager cleanup; test teardown interrupting the closing thread; timeouts in outer shutdown logic issuing interrupt while close() is awaiting executor termination.","solutions":["Avoid interrupting the thread performing close(); let the 10s awaitTermination complete during orderly shutdown.","Restore the interrupt status in the caller and proceed with resource cleanup; the executor was already shutdownNow()'ed so background tasks are stopping regardless.","If close() hangs near 10s regularly, audit long-period scheduled tasks in the ledger manager and shorten their cycles before shutdown.","Ensure only one component owns the lifecycle so close() is not invoked concurrently from competing shutdown paths."],"exampleFix":"// before\nledgerManager.close();\n// after\ntry {\n    ledgerManager.close();\n} catch (IOException e) {\n    Thread.currentThread().interrupt();\n    LOG.warn(\"Interrupted while closing ledger manager\", e);\n}","handlingStrategy":"try-catch","validationCode":"// Confirm the thread is not already interrupted before closing\nif (Thread.currentThread().isInterrupted()) {\n    // clear and re-close, or defer close to a non-interrupted thread\n    Thread.interrupted();\n}","typeGuard":null,"tryCatchPattern":"try {\n    ledgerManager.close();\n} catch (IOException e) {\n    Thread.currentThread().interrupt(); // flag was set by close(); keep it\n    LOG.warn(\"Ledger manager close interrupted; executor already shut down\", e);\n}","preventionTips":["Sequence shutdown so ledger manager close happens before interrupt-issuing phases","Allow the full awaitTermination window; do not preempt with interrupts","Give each resource a single lifecycle owner to avoid concurrent close() calls","Log and assert interrupt status in shutdown hooks to catch premature interrupts early"],"tags":["interruption","shutdown","threading","resource-cleanup"],"backgroundTag":"shutdown-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"}