Understanding the Druid Connection Pool Lifecycle and Management
This article provides a comprehensive walkthrough of Druid's connection pool lifecycle, detailing how getConnection initiates the process, the role of initialization, filter chains, connection validation, thread‑blocking strategies, and the various guardian threads that add, evict, and recycle connections to ensure performance and stability.
0. Class Diagram & Process Overview
This article uses getConnection as the entry point to explore the lifecycle of a connection within Druid. The overall workflow is divided into several main processes.
Main Process 1: Connection Acquisition
First, the flowchart shows that the pool initialization method init is called, then each filter in the responsibility chain is executed, and finally getConnectionDirect obtains the real connection object.
If testOnBorrow is enabled, each borrow triggers a MySQL long‑connection health check (default MySQL keep‑alive is 8 hours). Enabling this can degrade performance, so the default is false.
When testOnBorrow is false, Druid performs testWhileIdle checks based on timeBetweenEvictionRunsMillis (default 60 s). The idle time of a connection is compared with this interval to decide whether to run a validity test.
If testConnectionInternal returns false, the connection is considered broken and discardConnection is invoked, which may wake up the third main process to create a new connection. The loop repeats until a valid connection is obtained or the retry limit is reached.
Special Note ①
For best performance, keep testOnBorrow set to false and rely on the default 60 s idle check. Adjust timeBetweenEvictionRunsMillis only if the MySQL server’s keep‑alive period is shorter than 60 s.
Special Note ②
To avoid unnecessary pool expansion, set minIdle equal to maxActive for high‑QPS services, and enable keepAlive. For low‑QPS admin back‑ends that occasionally need many connections, keep minIdle lower than maxActive to allow dynamic scaling.
Main Process 2: Pool Initialization
If the pool is not yet initialized (checked via the boolean inited), init is invoked. During initialization, a global re‑entrant lock is created, double‑checked locking ensures thread safety, and filters annotated with @AutoLoad are loaded via SPI.
Three arrays are allocated with a capacity of maxActive: connections (stores actual connections), evictConnections (connections pending eviction), and keepAliveConnections (connections awaiting keep‑alive checks). An initial set of connections (defined by initialSize) is created, and two guardian threads are started for adding and removing connections.
Special Note ①
If the pool is not pre‑initialized, the first getConnection call will execute the full initialization flow, which can cause a noticeable delay under high concurrency. Pre‑warming the pool by calling init or getConnection beforehand is recommended.
Special Note ②
The global lock creates two Condition objects: empty (used by the add‑connection guardian when the pool has enough connections) and notEmpty (used by business threads waiting for a connection). When the pool is exhausted, a business thread blocks on notEmpty and wakes the add‑connection guardian.
Process 1.1: Responsibility Chain
Each DruidAbstractDataSource holds a filters list. When getConnection is called, the FilterChain invokes the corresponding method on every filter (e.g., dataSource_getConnection) before finally calling getConnectionDirect.
Process 1.2: Obtaining a Connection from the Pool
Druid supports two ways to add connections: a default guardian thread using await / signal, or an asynchronous thread pool when asyncInit=true. This article focuses on the default guardian thread.
If a connection is available, the pool simply decrements poolingCount, returns the connection, and increments activeCount (O(1) operation). If no connection is available, the guardian thread is signaled to create a new one, and the requesting thread enters a waiting loop controlled by maxWait and awaitNanos. If the wait expires, a SQLException is thrown.
How does Druid prevent excessive blocking when connections are exhausted?
Setting maxWaitThreadCount (default -1) limits the number of business threads that may block. When the current waiting thread count reaches this limit, Druid throws an exception instead of entering the wait state.
if (maxWaitThreadCount > 0 && notEmptyWaitThreadCount >= maxWaitThreadCount) {
connectErrorCountUpdater.incrementAndGet(this);
throw new SQLException("maxWaitThreadCount " + maxWaitThreadCount + ", current wait Thread count " + lock.getQueueLength());
}Process 1.3: Connection Validation
① init‑checker
During init, Druid creates a checker appropriate for the JDBC driver. For MySQL, if the driver provides a ping method, usePingMethod is set to true; otherwise Druid falls back to executing SELECT 1 to test the connection.
② testConnectionInternal
This method delegates to the checker’s isValidConnection. If usePingMethod is true, the driver’s ping is invoked via reflection; otherwise a SELECT 1 query is executed.
Process 1.4: Discarding a Bad Connection
If testConnectionInternal reports the connection as invalid, discardConnection is triggered, decreasing activeCount and closing the underlying JDBC Connection. If the connection is a DruidPooledConnection, its close method recycles the object.
Main Process 3: Add‑Connection Guardian Thread
This thread starts during pool initialization and mostly stays idle. When the pool is exhausted, it is signaled via empty.signal, creates new connections, and then wakes any threads blocked on notEmpty.
Main Process 4: Discard‑Connection Guardian Thread
4.1 Pool Shrinking and Connection Eviction
The thread calculates the eviction range as poolingCount - minIdle. Connections whose idle time exceeds minEvictableIdleTimeMillis (default 30 min) are added to evictConnections; those exceeding maxEvictableIdleTimeMillis (default 7 h) are evicted unconditionally.
If a connection is not in the eviction range but keepAlive is true, its idle time is compared with keepAliveBetweenTimeMillis (default 60 s). Exceeding this places the connection into keepAliveConnections for a health check.
After building the two queues, the pool compresses the remaining connections, closes those in evictConnections, and validates those in keepAliveConnections, discarding any that fail the check.
4.2 Active Connection Leak Prevention
When removeAbandoned=true, the pool periodically scans activeConnections. Connections that have been borrowed for too long are moved to abandonedList and forcibly closed, preventing memory leaks.
Main Process 5: Connection Recycling
The DruidPooledConnection.close() method triggers recycle. If removeAbandoned is true, the connection is removed from activeConnections. Any pending transaction is rolled back, statements and result sets are closed, and the connection is placed at the tail of connections with its lastActiveTimeMillis updated.
If testOnReturn were enabled (it is disabled by default), an additional validation would occur here, similar to testOnBorrow.
Conclusion
The article has outlined the complete lifecycle of a Druid connection—from pool initialization, through acquisition, validation, eviction, and recycling—highlighting the key configuration parameters that affect performance, reliability, and resource utilization.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
