Skip to content
Back to Blog
July 31, 2026

Connection Pooling Under Pressure

What happens to your database connections when everyone books their summer vacation at the same time — pool sizing, read/write separation, and circuit breaking.

Connection Pooling Under Pressure
M

Summer booking season starts in early March. Families plan vacations. Couples book anniversary trips. Groups coordinate destination weddings. Traffic goes up 3-5x in a matter of weeks.

Your database connections are the first bottleneck, and they fail in a sneaky way. The application doesn't crash. It just gets slow. Requests that normally take 50 milliseconds start taking 500 milliseconds because they're waiting for an available database connection. The pool is exhausted. Every connection is busy. New requests queue up, waiting.

The traveler doesn't see "error." They see the app being painfully slow. Which, for a booking platform, is almost as bad.

Why connection pools exist

Illustration for this section

Every database query requires a connection to the database server. Opening a connection involves a TCP handshake, TLS negotiation, authentication, and session setup. That takes 20-50 milliseconds. If you open and close a connection for every query, you spend more time managing connections than executing queries.

Connection pooling solves this by maintaining a set of pre-opened connections that get reused. A request borrows a connection, executes a query, and returns the connection to the pool. The next request reuses the same connection without the setup overhead.

The pool has a fixed size. Too small, and requests queue up waiting for connections. Too large, and you overwhelm the database server (each connection consumes memory and a process slot on the database server, and there's a hard limit).

Sizing for bursty traffic

Travel booking traffic is not steady. It's bursty. A single AI agent query might make 3-5 database calls in sequence (load user profile, check trip history, fetch booking details, update conversation state). Multiply that by concurrent travelers, and you get sharp, unpredictable demand patterns.

The pool needs to be large enough to handle burst concurrency without starving, but not so large that idle connections waste database resources.

Our sizing approach: take the peak concurrent database operations we've measured, add 20% headroom, and cap at the database's connection limit divided by the number of application instances. For us, this works out to a pool that's comfortable during normal traffic and tight but functional during seasonal peaks.

During peaks, we sometimes see connection wait times creep up. A few milliseconds of wait is fine. Persistent wait times above 100 milliseconds trigger investigation. We'd rather scale the application horizontally (more instances, each with its own pool) than push a single pool to its limit.

Separating read and write pools

Supporting diagram

This was one of our more impactful optimizations. We run separate connection pools for read and write operations.

The booking and payment path uses the write pool. These queries need strong consistency and must execute quickly. They can't be delayed by anything else.

Analytics queries, reporting, and the AI agent's "tell me about my past trips" queries use the read pool. These queries are often more complex (joins across multiple tables, aggregations) and can tolerate slightly higher latency.

Without separation, a slow analytics query (say, aggregating all bookings for a user with extensive travel history) holds a connection for 200 milliseconds. During that time, a booking query might be waiting for a connection. The analytics query is useful but not urgent. The booking query has a traveler waiting with their credit card ready.

With separate pools, analytics queries never compete with booking queries for connections. Each pool is sized for its workload. The write pool is smaller but has strict latency requirements. The read pool is larger and tolerates more variability.

AI agent queries are different

Here's something we discovered the hard way. AI agent queries hold database connections longer than typical CRUD operations.

A normal API request borrows a connection, runs a query, returns the connection. Total hold time: 5-20 milliseconds. The pool turns over quickly.

An AI agent request might make a sequence of database calls as part of its reasoning loop: load user preferences, check trip history, fetch relevant booking details, look up airport information. Each individual query is fast, but the agent might hold the connection across multiple sequential queries within a single turn of its reasoning. Total hold time: 50-200 milliseconds.

This difference matters for pool sizing. If your pool is sized for 5ms hold times and suddenly handles requests with 100ms hold times, the effective throughput drops 20x. We account for this by monitoring average connection hold time as a key metric and adjusting pool sizes when the workload mix changes (like when we ship a new agent feature that makes more database calls).

Monitoring pool health

We track four metrics for each connection pool:

Pool utilization is the percentage of connections currently in use. Steady-state utilization above 70% means we're uncomfortably close to exhaustion during spikes.

Wait time is how long a request waits for a connection when the pool is fully utilized. Any wait above 0 means the pool was briefly exhausted. Sustained waits mean it's undersized.

Checkout duration is how long a connection is held before being returned. An increase here means queries are getting slower or application code is holding connections longer than expected.

Timeout rate is the percentage of connection requests that time out. This is the metric that directly impacts travelers. A timeout means a request failed because it couldn't get a database connection in time.

These metrics feed into our health check endpoints. The deep health check verifies that a connection can be obtained from the pool within an acceptable time. If the pool is exhausted and the health check can't get a connection, the instance reports unhealthy and gets removed from the load balancer.

Circuit breaking on pool exhaustion

When the connection pool is fully utilized and the wait queue is growing, throwing more requests at it makes things worse. Each waiting request consumes a request worker thread, and those threads have their own limit. Pile up enough waiting requests and you exhaust the worker pool too, causing a cascade failure.

We implement a circuit breaker on the connection pool. When pool utilization hits a critical threshold and the wait queue exceeds a depth limit, new requests that require database access get rejected immediately with a "service busy" response. The client can retry after a brief delay.

This is intentionally aggressive. Rejecting requests early is better than accepting them and having them time out 30 seconds later. The traveler sees a quick "Please try again in a moment" instead of a hung loading screen that eventually errors.

The circuit breaker has a half-open state where it lets a trickle of requests through to test whether the pool has recovered. Once it has, normal traffic resumes.

Sizing your pool

If you're sizing connection pools for a booking platform, here's the formula that works for us.

Start with your database's maximum connection limit. Subtract connections reserved for administrative access, monitoring, and migrations. Divide the remainder by the number of application instances. That's your per-instance ceiling.

Within that ceiling, set the pool size based on measured peak concurrent database operations per instance. Add 20% headroom. If the result exceeds your per-instance ceiling, you need more application instances (horizontal scaling) rather than a bigger pool.

Monitor relentlessly. Pool sizing is not set-and-forget. Traffic patterns change. New features change query patterns. Seasonal peaks shift demand. Review pool utilization monthly and before any expected traffic spike.


Nowah is an AI travel agent that searches and books real flights and hotels through conversation — no filters, no thirty open tabs. Plan your next trip.

Share this article

Ready to Plan with Nowah?

Bring the idea. Nowah will help turn it into a trip.

Try Nowah