alibaba/druid · error · SQLException
maxWaitThreadCount {}, current wait Thread count {}
Error message
maxWaitThreadCount {}, current wait Thread count {} What it means
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.
Source
Thrown at core/src/main/java/com/alibaba/druid/pool/DruidDataSource.java:1623
} finally {
createDirect = false;
createDirectCountUpdater.decrementAndGet(this);
}
}
final ReentrantLock lock = this.lock;
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
connectErrorCountUpdater.incrementAndGet(this);
throw new SQLException("interrupt", e);
}
try {
if (maxWaitThreadCount > 0
&& notEmptyWaitThreadCount >= maxWaitThreadCount) {
connectErrorCountUpdater.incrementAndGet(this);
throw new SQLException("maxWaitThreadCount " + maxWaitThreadCount + ", current wait Thread count "
+ notEmptyWaitThreadCount);
}
if (onFatalError
&& onFatalErrorMaxActive > 0
&& activeCount >= onFatalErrorMaxActive) {
connectErrorCountUpdater.incrementAndGet(this);
StringBuilder errorMsg = new StringBuilder();
errorMsg.append("onFatalError, activeCount ")
.append(activeCount)
.append(", onFatalErrorMaxActive ")
.append(onFatalErrorMaxActive);
if (lastFatalErrorTimeMillis > 0) {
errorMsg.append(", time '")
.append(StringUtils.formatDateTime19(
lastFatalErrorTimeMillis, TimeZone.getDefault()))View on GitHub (pinned to fa8dc99126)
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.
Example fix
// before dataSource.setMaxActive(5); ddataSource.setMaxWaitThreadCount(2); // bursts of >2 waiters fail // after ddataSource.setMaxActive(50); ddataSource.setMaxWaitThreadCount(60); ddataSource.setRemoveAbandoned(true); ddataSource.setRemoveAbandonedTimeout(300);
Defensive patterns
Strategy: validation
Validate before calling
// before borrowing, confirm there is wait headroom
DruidDataSource dds = (DruidDataSource) dataSource;
long waiting = dds.getNotEmptyWaitThreadCount();
long limit = dds.getMaxWaitThreadCount();
if (limit > 0 && waiting >= limit) {
throw new ServiceUnavailableException("pool wait queue full: " + waiting + "/" + limit);
}
return dataSource.getConnection(); Try / catch
try {
return dataSource.getConnection();
} catch (SQLException e) {
if (e.getMessage() != null && e.getMessage().startsWith("maxWaitThreadCount")) {
// back off / shed load rather than hammer
throw new ServiceUnavailableException(e.getMessage(), e);
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- validationQuery didn't return a row
- connect error, url {}, driverClass {}
- dataSource inited.
- check connection info failed
- maxActive less than minIdle, {} < {}
AI-assisted analysis of alibaba/druid@fa8dc99126 (2026-08-14).
Data as JSON: /api/errors/54558eda1f561aa3.
Report an issue: GitHub.