Connection pools: 45ms to open a connection vs. 0.2ms to borrow one
TL;DR
Every new database connection goes through TCP, TLS and authentication handshakes — about 45ms in the video. A connection pool keeps connections open so they can be borrowed in ~0.2ms. When traffic exceeds the pool size, requests wait their turn.
Every time an application opens a new database connection, it has to do a TCP handshake, negotiate TLS and authenticate: in the video about 45ms before a single query can run. For a query that takes only 2ms, you pay a connection cost more than 20 times the actual work. A connection pool keeps a set of connections ready to borrow in about 0.2ms. When traffic exceeds the number of connections in the pool, requests have to wait their turn.
Three scenarios
| Scenario | Time to get a connection | Notes |
|---|---|---|
| No pool | ~45ms per request | TCP → TLS → AUTH repeated every time |
| Pool, light load | ~0.2ms | Borrow an existing connection, no new handshake |
| Burst of 14 requests, pool of 4 | 10ms, 20ms, 30ms… | 10 requests queue for a free connection |
After the burst, idle connections are closed by the idle timeout.
Key parameters
| Parameter | Meaning |
|---|---|
| Maximum size | Maximum concurrent connections to the database |
| Minimum size | Connections kept ready even when idle |
| Connection acquire timeout | How long to wait before erroring when the pool is exhausted |
| Idle timeout | How long before an idle connection is closed |
| Max lifetime | Maximum age of a connection, to avoid stale broken connections |
A bigger pool isn't necessarily faster
Databases have limits on connections and resources (CPU, memory, locks). Too many active connections contend with each other and slow everything down. A smaller pool that's just big enough often performs better. A sensible approach is to start small, measure, and grow only with evidence.
Common mistakes
- Connection leaks: not returning connections to the pool after use; the pool drains and the app hangs.
- Holding connections too long: opening a transaction, then doing other work (calling external APIs) before committing.
- Dead connections: after a database restart or network timeout; validate connections when borrowing.
- One pool per instance: many instances multiply the connection count and can exceed the database's limit.
How to apply it
- Always use a pool for apps with steady traffic (HikariCP for Java, pgBouncer for PostgreSQL, the built-in pool in most ORMs).
- Track "connection acquire time" as its own metric; when it rises, the pool is too small or queries hold connections too long.
- Serverless: per-instance pools easily exhaust DB connections; use a proxy such as RDS Proxy or pgBouncer.
- Keep transactions short so connections are returned quickly.
Sources & references
- About Pool Sizing — HikariCP