{"record":{"id":"870b427b7425cb60","repo":"apache/pulsar","slug":"managedledgerfactory-is-already-closed","errorCode":null,"errorMessage":"ManagedLedgerFactory is already closed.","messagePattern":"ManagedLedgerFactory is already closed\\.","errorType":"exception","errorClass":"ManagedLedgerException.ManagedLedgerFactoryClosedException","httpStatus":null,"severity":"error","filePath":"managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ManagedLedgerFactoryImpl.java","lineNumber":661,"sourceCode":"    void close(ManagedLedger ledger) {\n        // If the future in map is not done or has exceptionally complete, it means that @param-ledger is not in the\n        // map.\n        CompletableFuture<ManagedLedgerImpl> ledgerFuture = ledgers.get(ledger.getName());\n        if (ledgerFuture == null || !ledgerFuture.isDone() || ledgerFuture.isCompletedExceptionally()){\n            return;\n        }\n        if (ledgerFuture.join() != ledger){\n            return;\n        }\n        // Remove the ledger from the internal factory cache.\n        if (ledgers.remove(ledger.getName(), ledgerFuture)) {\n            entryCacheManager.removeEntryCache(ledger.getName());\n        }\n    }\n\n    public CompletableFuture<Void> shutdownAsync() throws ManagedLedgerException {\n        if (closed) {\n            throw new ManagedLedgerException.ManagedLedgerFactoryClosedException();\n        }\n        closed = true;\n\n        statsTask.cancel(true);\n        flushCursorsTask.cancel(true);\n        cacheEvictionExecutor.shutdownNow();\n\n        List<String> ledgerNames = new ArrayList<>(this.ledgers.keySet());\n        List<CompletableFuture<Void>> futures = new ArrayList<>(ledgerNames.size());\n        int numLedgers = ledgerNames.size();\n        log.info().attr(\"numLedgers\", numLedgers).log(\"Closing ledgers\");\n        for (String ledgerName : ledgerNames) {\n            CompletableFuture<ManagedLedgerImpl> ledgerFuture = ledgers.remove(ledgerName);\n            if (ledgerFuture == null) {\n                continue;\n            }\n            CompletableFuture<Void> future = new CompletableFuture<>();\n            futures.add(future);","sourceCodeStart":643,"sourceCodeEnd":679,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ManagedLedgerFactoryImpl.java#L643-L679","documentation":"ManagedLedgerFactoryImpl.shutdownAsync() throws ManagedLedgerFactoryClosedException when the factory's `closed` flag is already set. Once a ManagedLedgerFactory is shut down it cannot be reused; any further shutdown or open attempt on it is rejected. This is a lifecycle/state guard, not an infra failure.","triggerScenarios":"Calling shutdown()/shutdownAsync() twice on the same ManagedLedgerFactory; calling them after close() was already invoked; a shared factory instance shut down by one component (e.g. PulsarClient close triggering broker shutdown) then touched again by another.","commonSituations":"Double shutdown in cleanup paths; Pulsar broker restart hooks running twice; tests sharing a factory across suites and closing it in one suite's teardown.","solutions":["Guard shutdown with an idempotency check (AtomicBoolean) so double-shutdown is a no-op","Track factory lifecycle ownership — only the component that created the factory should close it, exactly once","If you need a working factory again, create a new ManagedLedgerFactory instance","If this comes from Pulsar shutdown ordering, ensure shutdown hooks are registered once, not per-module"],"exampleFix":"// before\nfactory.shutdown(); // may run twice\n// after\nprivate final AtomicBoolean shutDown = new AtomicBoolean(false);\nif (shutDown.compareAndSet(false, true)) {\n    factory.shutdown();\n}","handlingStrategy":"validation","validationCode":"private final AtomicBoolean shutdownOnce = new AtomicBoolean(false);\nif (!shutdownOnce.compareAndSet(false, true)) return; // skip second shutdown","typeGuard":null,"tryCatchPattern":"try {\n    factory.shutdownAsync();\n} catch (ManagedLedgerException.ManagedLedgerFactoryClosedException e) {\n    // already shut down — safe to ignore\n}","preventionTips":["Make shutdown idempotent with compareAndSet","Give exactly one component ownership of factory lifecycle","Never reuse a factory after close; create a new one","Register shutdown hooks once"],"tags":["lifecycle","managed-ledger","factory-closed","state"],"backgroundTag":"resource-already-closed","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}