{"record":{"id":"067639d33d7fca46","repo":"t8y2/dbx","slug":"jdbc-connection-pool-registry-is-closed","errorCode":null,"errorMessage":"JDBC connection pool registry is closed","messagePattern":"JDBC connection pool registry is closed","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"agents/common/src/main/java/com/dbx/agent/JdbcConnectionPoolRegistry.java","lineNumber":95,"sourceCode":"        this.checkoutExecutor = new JdbcCheckoutExecutor(\n            settings.globalMaximumPhysicalConnections,\n            connectionReleaseExecutor\n        );\n    }\n\n    Lease borrow(String identity, ConnectionFactory connectionFactory) throws Exception {\n        return borrow(identity, JdbcSessionRole.WORKLOAD, null, connectionFactory);\n    }\n\n    Lease borrow(String identity, JdbcSessionRole role, ConnectionFactory connectionFactory) throws Exception {\n        return borrow(identity, role, null, connectionFactory);\n    }\n\n    Lease borrow(String identity, JdbcSessionRole role, String connectionTestQuery, ConnectionFactory connectionFactory) throws Exception {\n        String key = digest(identity);\n        while (true) {\n            if (closed.get()) {\n                throw new IllegalStateException(\"JDBC connection pool registry is closed\");\n            }\n            SQLException failure = runtimeFailure.get();\n            if (failure != null) {\n                throw AgentRpcError.resource(\"close\", failure);\n            }\n            PoolEntry entry;\n            try {\n                entry = pools.computeIfAbsent(key, ignored -> createPoolEntry(key, connectionTestQuery, connectionFactory));\n            } catch (PoolCreationException error) {\n                throw error.unwrap();\n            }\n            try {\n                return entry.borrow(role);\n            } catch (PoolRetiredException ignored) {\n                pools.remove(key, entry);\n            } catch (AgentRpcError error) {\n                if (entry.isRetired()) {\n                    pools.remove(key, entry);","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/agents/common/src/main/java/com/dbx/agent/JdbcConnectionPoolRegistry.java#L77-L113","documentation":"JdbcConnectionPoolRegistry.borrow() refuses to hand out pool leases once the registry has been closed (close() sets the closed flag, shuts down the Hikari pools, executors, and budget). Any borrow() call after close() throws IllegalStateException(\"JDBC connection pool registry is closed\") because the internal executors and pools no longer exist.","triggerScenarios":"Calling borrow(identity, ...) on a registry instance after close() was invoked — e.g. an agent/shutdown hook closed the registry while background tasks or other threads still attempt to acquire connections.","commonSituations":"Application shutdown closes the registry while in-flight requests still try to borrow; a singleton registry was closed once (e.g. during a reconnect attempt or test teardown) and later reused; double-close race where one thread closes while another borrows.","solutions":["Treat the registry as single-use: after close(), create a new JdbcConnectionPoolRegistry for subsequent borrows.","Serialize lifecycle: stop all producers of borrow() calls (executors, schedulers) before calling close().","Check a shared closed/isRunning flag (or registry state) before borrowing, and route to a fresh registry if closed.","Catch IllegalStateException at the borrow site and reinitialize the registry + retry once."],"exampleFix":"// before\nLease lease = registry.borrow(identity, factory);\n// after\nif (registryClosed) {\n    registry = new JdbcConnectionPoolRegistry();\n    registryClosed = false;\n}\nLease lease = registry.borrow(identity, factory);","handlingStrategy":"retry","validationCode":"if (registryClosed) {\n    registry = new JdbcConnectionPoolRegistry();\n    registryClosed = false;\n}","typeGuard":"static boolean registryUsable(JdbcConnectionPoolRegistry r) {\n    return r != null && !r.isClosed(); // expose a state flag if not already available\n}","tryCatchPattern":"try {\n    return registry.borrow(identity, factory);\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"registry is closed\")) {\n        registry = new JdbcConnectionPoolRegistry();\n        return registry.borrow(identity, factory);\n    }\n    throw e;\n}","preventionTips":["Treat the registry as single-use; recreate rather than reuse after close().","Shut down all borrow() producers before calling close() in shutdown hooks.","Centralize registry lifecycle in one owner so close() and borrow() cannot race.","In tests, build a fresh registry per test instead of closing a shared one."],"tags":["connection-pool","lifecycle","jdbc","shutdown"],"backgroundTag":"pool-already-closed","analyzedSha":"c0390bff16418b651f4728520d99adf8ce48829a","analyzedAt":"2026-09-05T23:05:10.900Z","contentChangedAt":"2026-09-05T23:05:10.900Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}