Yashveer Singh
Connect
<- All posts
Performance Optimization11 min read

Connection Pooling: Why Defaults Are Wrong for Most Stacks

Framework default connection pool sizes are designed for a generic workload. Yours is not generic. The right pool size depends on your concurrency, your database capacity, your query latency, and your worker count. The defaults are usually too small for production loads and sometimes too large for the database to support. Picking the right numbers requires a small amount of measurement and a small amount of math.

Written by Yashveer Singh, founder of Yashveer Labs.

What you actually need to know

  • Framework defaults are conservative and almost never right.
  • Size the pool for your concurrency at the 99th percentile.
  • Verify total connections fit under the database limit.
  • Long queries need a larger pool relative to worker count.
  • Each service has its own pool. Total budget across services matters.
StackDefaultTypical correct value
Node.js pg1020 to 50
Rails ActiveRecord515 to 40
DjangoVariable15 to 40
Java HikariCP1020 to 50
PgBouncer transaction modeVariableSized for concurrent transactions

The core argument

Framework defaults for connection pooling exist because the framework cannot know your workload. The default is a reasonable starting point for a generic small application. Your application is not generic. Your application is specific to its concurrency, its query patterns, and its database capacity. The default is the starting point. Tuning is the work.

The teams that run on defaults discover the problem when traffic grows. The pool that worked at one hundred concurrent users does not work at one thousand. The application stalls under load. The database is fine. The pool is the bottleneck. The team scales the database. The bottleneck remains because it was never the database. The team is confused and the customers are unhappy.

The right approach is to size the pool deliberately. Measure the concurrency at peak load. Pick a pool size that handles the 99th percentile of concurrency. Verify the total across servers fits under the database's max_connections divided by two. Reserve the other half for migrations, admin queries, and one off operations.

The other piece is the queue behavior. When the pool is full and new requests arrive, what happens. Wait. Time out. Reject. The default is usually wait forever, which produces hung requests. The right behavior is to wait a few seconds then time out, so the failure surfaces quickly rather than appearing as a slow response.

The tuning workflow

StepDetail
Measure concurrencyDatabase connections in use at peak load
Estimate growthWhere will concurrency be in six months
Compute pool sizeConcurrency at growth peak plus small buffer
Verify database capacityTotal pool across servers under max_connections / 2
Set acquisition timeoutA few seconds
Set connection lifetime30 minutes to a few hours
Add monitoringCheckout time, queue depth, errors
Test under loadConfirm the new size handles peak

How much does this cost

The cost of tuning is engineering time. A few hours to measure and adjust. The infrastructure cost difference is negligible. The savings show up as fewer incidents and lower latency under load.

Features the pool tuning must have

  • A measurement of current concurrency.
  • A pool size set deliberately based on the measurement.
  • An acquisition timeout that surfaces failures quickly.
  • A connection lifetime that prevents stale connections.
  • Monitoring on the three key metrics.
  • An alarm on saturation.
  • A documented review cadence as the workload changes.

Expert opinion

The teams that have never tuned their connection pool are usually a traffic spike away from a hard to diagnose incident. The tuning is mechanical and cheap. The incident is expensive and confusing. The discipline of measuring concurrency and sizing the pool deliberately is one of the highest leverage observability practices in any production stack.

>

Yashveer Singh, founder of Yashveer Labs

How this played out on a real project

A client SaaS was running on the Node.js pg library default pool of 10. They had four application servers. Their Postgres allowed 100 connections. At peak load they were experiencing latency that they had attributed to slow queries.

The pool checkout time at peak was 600 milliseconds. The slow queries were actually fine. The pool was undersized.

We measured concurrency. The peak was around 30 concurrent operations per server. We raised the pool to 40 per server. Total connections were 160 across four servers, which was above the Postgres limit. We added PgBouncer in transaction mode with a pool of 50 to Postgres. The application could have 160 connections to PgBouncer. PgBouncer maintained 50 connections to Postgres.

The latency at peak dropped dramatically. The checkout time fell to under 5 milliseconds. The database was no longer near its connection limit. The fix took a day. The improvement was immediate.

For more on the related work, see connection pooling the quiet killer of SaaS performance and PostgreSQL performance at scale the tweaks that move the needle.

Common mistakes teams make

  1. Running on framework defaults.
  2. Sizing the pool to match worker count instead of concurrency.
  3. No measurement before tuning.
  4. No PgBouncer for serverless workloads.
  5. Acquisition timeout set to infinite.
  6. No monitoring on the pool.
  7. Pool total across servers exceeds database limit.
  8. No documented review cadence as load grows.

A 30 day plan

  1. Week one. Measure current concurrency at peak.
  2. Week two. Compute and apply the right pool size.
  3. Week three. Add PgBouncer or equivalent if needed.
  4. Week four. Add monitoring and alarms. Test under synthetic load.

For more on the related work, read connection pooling the quiet killer of SaaS performance and database query performance the five patterns that hurt the most. On the broader performance side, backend performance budgets how to set them is the natural next read.

FAQ

Frequently asked

Author

Why Yashveer Singh is the call for this work

I have spent the last four years writing software that runs in production. Three live client sites. A Roblox game with real players. Nexli, a school management system about to launch into private testing. Nyxera, a fully local AI assistant. Most people writing about this topic are summarizing other people's blog posts. I am writing from the codebase. If you want this kind of work done right, I am the person you call. Yashveer Singh, founder of Yashveer Labs.

Related reading