{"record":{"id":"54558eda1f561aa3","repo":"alibaba/druid","slug":"maxwaitthreadcount-current-wait-thread-count","errorCode":null,"errorMessage":"maxWaitThreadCount {}, current wait Thread count {}","messagePattern":"maxWaitThreadCount (.+?), current wait Thread count (.+?)","errorType":"exception","errorClass":"SQLException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/com/alibaba/druid/pool/DruidDataSource.java","lineNumber":1623,"sourceCode":"                } finally {\n                    createDirect = false;\n                    createDirectCountUpdater.decrementAndGet(this);\n                }\n            }\n\n            final ReentrantLock lock = this.lock;\n            try {\n                lock.lockInterruptibly();\n            } catch (InterruptedException e) {\n                connectErrorCountUpdater.incrementAndGet(this);\n                throw new SQLException(\"interrupt\", e);\n            }\n\n            try {\n                if (maxWaitThreadCount > 0\n                        && notEmptyWaitThreadCount >= maxWaitThreadCount) {\n                    connectErrorCountUpdater.incrementAndGet(this);\n                    throw new SQLException(\"maxWaitThreadCount \" + maxWaitThreadCount + \", current wait Thread count \"\n                            + notEmptyWaitThreadCount);\n                }\n\n                if (onFatalError\n                        && onFatalErrorMaxActive > 0\n                        && activeCount >= onFatalErrorMaxActive) {\n                    connectErrorCountUpdater.incrementAndGet(this);\n\n                    StringBuilder errorMsg = new StringBuilder();\n                    errorMsg.append(\"onFatalError, activeCount \")\n                            .append(activeCount)\n                            .append(\", onFatalErrorMaxActive \")\n                            .append(onFatalErrorMaxActive);\n\n                    if (lastFatalErrorTimeMillis > 0) {\n                        errorMsg.append(\", time '\")\n                                .append(StringUtils.formatDateTime19(\n                                        lastFatalErrorTimeMillis, TimeZone.getDefault()))","sourceCodeStart":1605,"sourceCodeEnd":1641,"githubUrl":"https://github.com/alibaba/druid/blob/fa8dc9912637a2f729eef9f55356621fec18d40e/core/src/main/java/com/alibaba/druid/pool/DruidDataSource.java#L1605-L1641","documentation":"A back-pressure guard: if maxWaitThreadCount is configured (>0) and the number of threads currently blocked waiting for a free connection (notEmptyWaitThreadCount) has reached or exceeded that limit, getConnection() refuses the new caller and throws. The point is to fail fast instead of letting an unbounded queue of waiters pile up and exhaust request threads.","triggerScenarios":"maxWaitThreadCount set to N, and N+1 or more threads are simultaneously parked inside getConnection() waiting for a pooled connection. Triggered by a burst of concurrent requests larger than the configured wait allowance, typically combined with an undersized maxActive pool or long-running queries holding connections.","commonSituations":"maxActive too small for real traffic; connection leak (connections borrowed but never closed in finally) shrinking the effective pool; a slow/stuck query holding all connections; maxWaitThreadCount left at a low default while load grew; transient DB stalls causing all workers to pile up.","solutions":["Raise maxWaitThreadCount (or set it to 0 to disable the guard) once you confirm the pool genuinely needs more concurrent waiters — but first verify the pool is sized right.","Increase maxActive / minIdle so fewer threads actually have to wait; size maxActive to (peak_qps * avg_query_seconds) plus headroom.","Hunt for connection leaks: ensure every getConnection() is closed in try-with-resources; enable removeAbandoned=true with a conservative removeAbandonedTimeout.","Profile slow SQL holding connections; the message reports current wait count, so cross-reference with Druid's runningSqlList to find the blocker."],"exampleFix":"// before\ndataSource.setMaxActive(5);\nddataSource.setMaxWaitThreadCount(2); // bursts of >2 waiters fail\n\n// after\nddataSource.setMaxActive(50);\nddataSource.setMaxWaitThreadCount(60);\nddataSource.setRemoveAbandoned(true);\nddataSource.setRemoveAbandonedTimeout(300);","handlingStrategy":"validation","validationCode":"// before borrowing, confirm there is wait headroom\nDruidDataSource dds = (DruidDataSource) dataSource;\nlong waiting = dds.getNotEmptyWaitThreadCount();\nlong limit = dds.getMaxWaitThreadCount();\nif (limit > 0 && waiting >= limit) {\n    throw new ServiceUnavailableException(\"pool wait queue full: \" + waiting + \"/\" + limit);\n}\nreturn dataSource.getConnection();","typeGuard":null,"tryCatchPattern":"try {\n    return dataSource.getConnection();\n} catch (SQLException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"maxWaitThreadCount\")) {\n        // back off / shed load rather than hammer\n        throw new ServiceUnavailableException(e.getMessage(), e);\n    }\n    throw e;\n}","preventionTips":["Size maxActive to peak_qps * avg_hold_seconds + headroom so waiters rarely accumulate.","Enable removeAbandoned to reclaim leaked connections that starve the pool.","Set maxWaitThreadCount deliberately (not by accident) to match your request-thread budget."],"tags":["backpressure","connection-pool","capacity","getconnection","config"],"backgroundTag":null,"analyzedSha":"fa8dc9912637a2f729eef9f55356621fec18d40e","analyzedAt":"2026-08-14T04:55:06.789Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}