Altamar Labs
Back to Blog
reliability· 2 min read

Connection pooling: the invisible optimization that decides if your system scales

Opening a new connection per request looks harmless until traffic grows. Why connection pooling is one of the most underrated performance decisions.

Opening a connection, whether to a database or an external HTTP service, isn't a free operation. It involves a handshake, in many cases TLS negotiation, and resource allocation on both the client and server side. Doing it once per request is a decision that works perfectly in local development, with one user and no real load, and becomes a silent bottleneck the moment traffic grows.

The cost you don't see until it's too late

The problem with opening a new connection per request isn't just the added latency on each individual call. Under concurrent load, the system starts competing with itself for limited resources: available sockets, server-side connection limits, CPU time spent negotiating TLS over and over to talk to the same destination. At some point, the system stops being bottlenecked by the actual work it does and starts being bottlenecked by the cost of establishing the connections needed to do that work.

This is particularly insidious because it doesn't show up as an error. It shows up as gradual degradation: the system responds slower and slower as concurrency increases, with no error message pointing to the real cause.

What a connection pool actually solves

A connection pool keeps a set of already-established connections and reuses them across requests, instead of creating and destroying one per operation. This turns the cost of establishing a connection into a cost paid a few times, not once per request.

But a poorly sized pool introduces its own problems, different from and sometimes harder to diagnose than having no pool at all:

  • A pool that's too small, under high concurrency, creates a queue of requests waiting for a free connection, which shows up as high latency even though the destination resource (the database, the external service) is working perfectly fine.
  • A pool that's too large can overwhelm the destination service with more concurrent connections than it can comfortably handle, producing the same kind of degradation you were trying to avoid, now on the server side.

The question that actually needs answering

Sizing a pool isn't guessing a reasonable number and hoping it works. It's answering, with real data, how much genuine concurrency the system needs at its expected traffic peak, and how much concurrency the destination resource can tolerate before degrading. That number is almost never the library or framework's default, and almost never "bigger is better."

The lesson that generalizes

Connection pooling is a perfect example of a category of optimizations rarely taught until a system has real traffic: they don't change what the system does, they change how much it costs to do it repeatedly. Most systems that "don't scale" don't fail because of an inefficient algorithm. They fail because nobody measured the cost of repetitive low-level operations, like opening a connection, until the bill arrived in the form of unexplained latency under load.