Skip to content
← All issues

Readyset Weekly: Issue 49

Connection Pooling: An Overlooked Bottleneck in High-Concurrency Systems

Most database performance conversations start with queries: slow joins, missing indexes, unoptimized aggregations. Connection pooling rarely makes that list, which is exactly why it becomes a problem. Every new database connection incurs overhead from a TCP handshake, TLS negotiation, authentication, and session setup, and under high concurrency, that compounds into a real bottleneck. Connection pooling addresses this by reusing established connections instead of opening new ones per request. Getting the configuration right is where most teams go wrong.

  • Pool size is not a "bigger is better" problem. The instinct is to set a high maximum pool size to ensure connections are always available. In practice, too many concurrent connections overwhelm the database with context switching and memory overhead, eating into the performance gains you were trying to capture. For most workloads, smaller pools perform better than oversized ones. The right number depends on your database's capacity and how many application instances are connecting to it.
  • Connection leaks are quiet and cumulative. If a connection is checked out from the pool but never returned, due to an exception, a code path that skips cleanup, or a long-running transaction that holds the connection idle, the pool drains slowly. Under normal load, this might not surface. Under peak load, it causes new requests to queue or fail entirely. Proper connection validation and timeout configuration is not optional at scale.
  • Transaction mode vs. session mode pooling behave very differently. Tools like PgBouncer offer both. Session mode assigns a connection for the duration of the session. Transaction mode reassigns connections between transactions, enabling much higher concurrency from a smaller pool. The tradeoff is that session-level features like prepared statements and advisory locks behave differently in transaction mode. Choosing the wrong mode introduces subtle correctness issues that are hard to trace.
  • Pooling is not a substitute for reducing query load. A well-configured connection pool helps your database handle concurrency more efficiently. It does not reduce the number of queries hitting the database or the cost of executing them. Teams that treat pooling as a scaling solution often find they have deferred the real problem rather than solved it.

Connection pooling is an infrastructure that disappears when it is configured correctly and becomes very visible when it is not. Getting the configuration right, pool size, timeouts, mode, and validation, is a cost-effective performance win. But pooling only manages connections. Once query volume or execution cost becomes the bottleneck, you need to reduce the work hitting the database, not just manage access to it.

🔍 What We're Watching in the Database Ecosystem

MAx Cache Stable Release: 3x Faster WordPress Page Loads on Apache and Nginx

CloudLinux's MAx Cache moves WordPress page caching out of PHP and into the web server itself, bypassing PHP entirely on cache hits. The result is a 3x improvement in requests per second and up to 80% reduction in CPU and memory usage on Nginx. An interesting look at what happens when you push the caching layer as close to the request as possible, a principle that applies well beyond WordPress. But once requests still require database work, the bottleneck shifts to query execution itself. In our WordPress test, query-level caching offloaded most of the read workload and reduced database impact to near-zero without code changes.

A Practical Guide to PostgreSQL Indexes

Percona's breakdown of PostgreSQL index types, B-tree, Hash, GIN, GiST, BRIN, and when to reach for each. Covers partial indexes, covering indexes, expression indexes, and the maintenance overhead that comes with over-indexing. Useful reference for anyone who has ever added an index hoping to fix a slow query and made things worse.

In Case You Missed It

We are hosting a live walkthrough of Readyset Shallow Cache, how it works, what it is designed for, and what it means for your stack. We don't want you to miss this! Register here.

And if you have been following the binary log series: Part 4 is live, Marcelo dives into the Previous GTIDs Log Event, the event that makes MySQL replication efficient by telling replicas exactly where to start without scanning the entire log.

Get Readyset Weekly in your inbox

One email a week — what shipped, what we learned, and what is worth reading.