Skip to content

// Data & Databases · 10

Connection Pooling

Open a connection per queryleads toReuse a warm pool

Reuse a small set of open connections instead of opening new ones.

How it fits together

Connection Pooling structureApp instances10 instancespool size = 8total 80 connections«external»Connection pooleroptionaltransaction modeLeaked connectionnever closedDatabaseconnection limit = 100directpooledfewer connsnot closedholds a slot
uses / callsfails / error path
  • App instances uses / calls Database (direct)
  • App instances uses / calls Connection pooler (pooled)
  • Connection pooler uses / calls Database (fewer conns)
  • App instances fails / error path Leaked connection (not closed)
  • Leaked connection fails / error path Database (holds a slot)

Spot it

  • Timeouts obtaining a connection from the pool
  • The database hits its connection limit under load
  • Scaling out the app makes the database worse

It costs

A bigger pool is not faster; too many connections slow the database down.

In code

// Pool size is part of the connection string, e.g. "...;Maximum Pool Size=20" for Npgsqlpublic sealed class OrderReader(string connectionString){    public async Task<int> CountOpenAsync(CancellationToken ct)    {        await using var connection = new NpgsqlConnection(connectionString);        await connection.OpenAsync(ct);        await using var command = new NpgsqlCommand(            "SELECT COUNT(*) FROM orders WHERE status = 'open'", connection);        return Convert.ToInt32(await command.ExecuteScalarAsync(ct));    }}
Go deeperThe full explanation and what to ask for · 1 min readExpand

Opening a connection costs a TCP handshake, TLS and authentication, and in PostgreSQL a whole server process. Drivers pool connections per connection string automatically: closing or disposing returns the connection to the pool instead of closing it.

Exhaustion is usually a leak or a hold, not a pool that is too small: a connection never closed, a transaction left open across an HTTP call, or blocked threads holding connections. Raising the pool size hides the leak and moves the pressure onto the database.

Total connections are instances × pool size, and must fit under the server limit with headroom. With many instances or serverless, put a connection pooler in front; transaction mode multiplexes best but breaks session-level features such as SET, LISTEN and session advisory locks.

Ask for it

  • maximum pool size
  • pool exhaustion
  • connection leak
  • server connection limit
  • connection pooler
  • idle connection timeout

We get pool timeouts under load. Check for connection leaks (missing disposal, sync-over-async, transactions held across HTTP calls), size the maximum pool size so instances × pool fits under the server connection limit, and say whether we need a pooler (PgBouncer or pgpool).