+91 98726 60544 hello@mitstech.co Mon–Sat · 09:00–18:30 IST

Connection pooling: the limit you hit before CPU

Cloud By Mits Engineering Team 2 min read
Connection pooling: the limit you hit before CPU

A common scaling story: traffic grows, response times worsen, more application instances are added, and the database gets slower rather than faster. The instinct is that the database needs a bigger machine. Often it needs fewer connections, and understanding why is one of the higher-leverage pieces of knowledge in operating a database.

Each PostgreSQL connection is a separate operating system process with its own memory, and each one competes for the same limited CPU and disk. Past a certain point, adding connections does not add throughput - it adds contention, context switching and lock waiting, and total throughput falls while every individual query gets slower. The database is doing more work and completing less.

The arithmetic that surprises people is how few connections a database actually wants. A widely used starting formula is roughly twice the number of CPU cores plus the effective spindle count, which on an eight-core machine lands somewhere near twenty. Twenty. Meanwhile a typical deployment has ten application instances each with a pool of twenty, offering four hundred connections to a database that performs best with a small fraction of that.

This is why the pool has to be sized across the fleet rather than per instance. Total connections equals pool size times instances, and that total is what the database sees. Autoscaling makes it worse: each new instance brings its own pool, so a traffic spike that triggers scaling also multiplies connection pressure at exactly the moment the database is busiest.

The tool for this is an external pooler - PgBouncer for PostgreSQL, ProxySQL for MySQL - sitting between the application and the database. Applications connect to it freely; it maintains a small pool of real connections and multiplexes across them. In transaction mode this works extremely well, with one caveat worth knowing before you enable it: anything relying on session state across transactions, such as prepared statements, session variables or advisory locks, will misbehave, because your next transaction may land on a different backend.

Two settings do most of the remaining damage when left at defaults. A statement timeout, so a single runaway query cannot hold a connection indefinitely and starve everything behind it. And a connection acquisition timeout in the application, so a request that cannot get a connection fails quickly with a clear error rather than piling up threads waiting - which is how a database slowdown becomes an application outage several layers away from the actual cause.

Need help with this? Explore our Cloud Solutions & Migration services. Learn more Back to all news

Keep reading

More on Cloud