Distinguishing connection pool exhaustion from thread pool queuing in MariaDB
0 reputation · 08 Feb 2023, 15:23 UTC
0 reputation · 08 Feb 2023, 15:23 UTC
Designing monitoring for intermittent connection limits in MariaDB requires separating true connection exhaustion from queuing behavior introduced by the thread pool plugin.
The documented limits involve max_connections as a hard ceiling, wait_timeout governing idle session lifetime, and the thread_pool_size controlling how many worker threads service incoming requests. Sleep state connections can hold pool slots without active I/O, and a mismatch between server wait_timeout and application pool maxLifetime is a known source of stale connections.
Thread pool starvation can mimic exhaustion symptoms when thread_pool_size is low, making it unclear whether a limit is reached at the connection layer or the request handling layer. Synchronized visibility of application pool metrics and server process states is needed for reliable diagnosis.
Which server status indicators reliably differentiate a max_connections limit from thread pool queuing under load? Does thread_pool_size interact with max_connections in a way that changes the observed error behavior? Can wait_timeout and application pool lifetime be compared without assuming a specific client library implementation?
35275 reputation · 08 Feb 2023, 18:28 UTC
When a client receives 1040 Too many connections, the server has hit max_connections. If the same load produces latency spikes but no 1040, the bottleneck is the thread pool queue.
max_connections is a hard ceiling on authenticated sessions (including SLEEP ones). Reaching it triggers ER_CON_COUNT_ERROR (1040) and increments Connection_errors_max_connections.thread_handling=pool-of-threads) only limits concurrent query execution. When all workers are busy, new requests queue inside the pool; the connection remains established and still counts toward max_connections.Thread_pool_queued_threads and may cause client‑side timeouts, but it never produces a 1040 error.A small thread_pool_size under heavy concurrency can fill max_connections with idle or queued sessions. The application then sees refused connections that look like exhaustion, while the server is actually thread‑starved.
Connection_errors_max_connections > 0 and immediate 1040 on connect = max_connections hit.Thread_pool_queued_threads > 0, Threads_running ≈ thread_pool_size, and no 1040 = thread‑pool queuing.Threads_connected with low Threads_running points to a buildup of SLEEP connections.Thread_pool_active_threads, Thread_pool_idle_threads, and Thread_pool_stall_count for worker saturation.Changing thread_pool_size does not alter the max_connections ceiling. However, a too‑small pool can cause many connections to sit in the queue, filling the max_connections limit and making exhaustion appear as a side effect of queuing.
Compare server‑side session age (SHOW PROCESSLIST) with wait_timeout (or interactive_timeout for interactive clients). If a SLEEP connection’s Time exceeds the timeout, the server has already closed it, but the client pool may still hold the socket. Client pool lifetimes can be inferred from pool metrics (eviction timestamps, idle counts) and aligned with server timestamps for a generic comparison.
SHOW GLOBAL VARIABLES LIKE 'thread_handling';
SHOW GLOBAL VARIABLES LIKE 'max_connections';
SHOW GLOBAL VARIABLES LIKE 'thread_pool_size';
SHOW GLOBAL STATUS LIKE 'Thread_pool_%';
SHOW GLOBAL STATUS LIKE 'Connection_errors_max_connections';
SHOW GLOBAL STATUS LIKE 'Threads_connected';
SHOW GLOBAL STATUS LIKE 'Threads_running';
Run the above queries under load that is below max_connections but above thread_pool_size; you should see Thread_pool_queued_threads rise while Connection_errors_max_connections stays zero. If Connection_errors_max_connections increments, the problem is true connection exhaustion.
One missing diagnostic detail that changes the recommendation: is thread_handling set to pool-of-threads on the target server? If not, Thread_pool_% variables are absent and queuing occurs at the OS/network layer, requiring a different set of metrics.
Use comments to ask for clarification. Post a solution as an answer.
35,275 reputation · 08 Feb 2023, 18:25 UTC
The clearest server-side signal is the pair Threads_connected versus Threads_queued from SHOW GLOBAL STATUS. When Threads_connected hits max_connections, new connection attempts fail immediately with ER_CON_COUNT_ERROR (1040). When the thread pool is saturated, Threads_connected stays below the ceiling while Threads_queued grows; clients then experience ER_LOCK_WAIT_TIMEOUT (1205) or a hang until thread_pool_stall_limit triggers.
thread_pool_size does not change the max_connections ceiling—it only governs how many of those connections can execute simultaneously. Idle connections in Sleep still count toward Threads_connected, so a mismatch between server wait_timeout and the application pool’s maxLifetime can fill the connection table without ever stressing the thread pool.
Verify the plugin is loaded (SHOW PLUGINS LIKE 'thread_pool'); it is optional before MariaDB 10.5 and absent in some distributions, which changes the queuing behavior entirely.