{"record":{"id":"fc79f6ae19c84106","repo":"t8y2/dbx","slug":"interrupted-while-waiting-for-the-jdbc-physical-co","errorCode":null,"errorMessage":"Interrupted while waiting for the JDBC physical connection budget","messagePattern":"Interrupted while waiting for the JDBC physical connection budget","errorType":"exception","errorClass":"SQLException","httpStatus":null,"severity":"warning","filePath":"agents/common/src/main/java/com/dbx/agent/JdbcConnectionPoolRegistry.java","lineNumber":1683,"sourceCode":"    }\n\n    private static final class PhysicalConnectionBudget {\n        private final int maximum;\n        private final Semaphore permits;\n\n        private PhysicalConnectionBudget(int maximum) {\n            this.maximum = maximum;\n            this.permits = new Semaphore(maximum, true);\n        }\n\n        private void acquire(OperationDeadline deadline) throws SQLException {\n            try {\n                if (permits.tryAcquire(deadline.remainingNanos(), TimeUnit.NANOSECONDS)) {\n                    return;\n                }\n            } catch (InterruptedException error) {\n                Thread.currentThread().interrupt();\n                throw new SQLException(\"Interrupted while waiting for the JDBC physical connection budget\", error);\n            }\n            throw new PhysicalConnectionLimitException(maximum);\n        }\n\n        private Connection wrap(\n            Connection connection,\n            PhysicalConnectionCloser physicalConnectionCloser,\n            ConnectionFactoryDataSource factoryDataSource,\n            long closeTimeoutMillis,\n            ConnectionFactoryDataSource.HikariSetupAttempt setupAttempt\n        ) {\n            AtomicBoolean released = new AtomicBoolean();\n            return (Connection) Proxy.newProxyInstance(\n                JdbcConnectionPoolRegistry.class.getClassLoader(),\n                new Class<?>[] {Connection.class, HikariSetupTrackedConnection.class},\n                (proxy, method, arguments) -> {\n                    if (\"completeHikariSetup\".equals(method.getName()) && method.getParameterCount() == 0) {\n                        setupAttempt.completeSuccessfully();","sourceCodeStart":1665,"sourceCodeEnd":1701,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/agents/common/src/main/java/com/dbx/agent/JdbcConnectionPoolRegistry.java#L1665-L1701","documentation":"Thrown when the thread waiting to acquire a physical connection budget permit (semaphore limiting concurrent physical connections) is interrupted. The interrupt flag is restored and the interruption is wrapped in a SQLException. It indicates caller-side cancellation, not pool exhaustion.","triggerScenarios":"All physical-connection permits are held and the caller blocks in permits.tryAcquire(deadline) inside the physical-connect path, then another thread interrupts it (executor.shutdownNow(), request cancellation, shutdown hook).","commonSituations":"Graceful shutdown while the pool is at its physical connection limit; cancelled HTTP requests whose worker threads were queued waiting for connection budget; timeouts implemented via thread interruption.","solutions":["Treat it as cancellation: check the InterruptedException cause and unwind gracefully","Avoid interrupting pool worker threads during shutdown; drain/await outstanding checkouts first","Increase maximum physical connections to reduce time threads spend blocked on permits","Use the deadline mechanism (timeout) instead of interrupt() to bound waiting"],"exampleFix":"// before\nexec.shutdownNow();\n// after\npool.drain(timeout); // let in-flight checkouts finish before shutdownNow\nexec.shutdownNow();","handlingStrategy":"try-catch","validationCode":"if (Thread.currentThread().isInterrupted()) {\n    throw new CancellationException(\"skip physical connect: already interrupted\");\n}","typeGuard":"static boolean isBudgetInterruption(SQLException e) {\n    return e.getCause() instanceof InterruptedException\n        && e.getMessage() != null\n        && e.getMessage().contains(\"physical connection budget\");\n}","tryCatchPattern":"try {\n    conn = pool.checkout();\n} catch (SQLException e) {\n    if (isBudgetInterruption(e)) {\n        Thread.currentThread().interrupt();\n        throw new CancellationException(e);\n    }\n    throw e;\n}","preventionTips":["Use deadline-based waiting rather than interrupting worker threads","During shutdown, stop new submissions and await in-flight physical connects before shutdownNow","Raise the physical connection limit so threads block less often on permits","Propagate (never swallow) the interrupt flag after catching"],"tags":["jdbc","interruption","semaphore","concurrency"],"backgroundTag":"thread-interrupted","analyzedSha":"c0390bff16418b651f4728520d99adf8ce48829a","analyzedAt":"2026-09-05T23:05:10.900Z","contentChangedAt":"2026-09-05T23:05:10.900Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}