The database rejects connections. How should you size application pools?

Why adding backend processes can exhaust database connections. Pool budgets, queued requests and a careful approach to PostgreSQL.

Blue and grey Ethernet cables plugged into a numbered network patch panel
Photography: Jordan Harrison / Unsplash

Scaling a backend can introduce a problem that a CPU graph does not explain: the application waits for a database connection or PostgreSQL rejects new ones. Each process may maintain its own pool, a set of connections available for reuse. Assess its limit together with every other database client. The following approach uses PostgreSQL 18; examples of client metrics use node-postgres.

Find out where the request is waiting

Distinguish waiting in the application pool from a failure to establish a new connection to the server. In node-postgres, track waitingCount alongside totalCount and idleCount. A growing queue is a reason to investigate who holds connections and for how long. Measure query duration separately from the time needed to acquire a connection.

In PostgreSQL, check the setting with SHOW max_connections; and inspect reserved connection slots. Raising this limit requires a server restart and increases some resource allocations, including shared memory. During an incident, first identify the actual source of pressure and the options for a controlled intervention.

Count the pools across every process

For direct database connections, add the maximum pool sizes of all processes. Include workers, scheduled jobs and other services. An illustrative example: four processes with a limit of 20 connections each can open up to 80 connections in total. This is capacity arithmetic, not a measurement of a particular system or a recommended pool size.

Account for old and new versions running together during a deployment, as well as the upper limit of automatic scaling. Leave room for administration and diagnostics. Choose each process limit from that budget and load measurements; a web service and a batch processor may need different settings.

Investigate connections held for a long time

In pg_stat_activity, compare application_name, state and transaction start time. Idle means the session is waiting for a new command. Idle in transaction means an open transaction with no query currently running and warrants investigation. Some diagnostic details about other sessions require appropriate privileges; a missing value does not prove that no problem exists.

When you acquire a client manually in node-postgres, ensure it returns to the pool after errors too, for example through finally. Also configure a finite wait for acquiring a connection. If you need to terminate a specific database session, identify its owner and impact first; pg_terminate_backend ends the entire session. Terminating connections indiscriminately is a poor first intervention.

Test the change with realistic concurrency

If you use PgBouncer, distinguish client connections from connections to PostgreSQL. The default_pool_size parameter sets the default limit for each user and database pair; with multiple pairs and instances, assess their combined limits. Transaction pooling returns the server connection after a transaction, but requires application compatibility checks, for example when using LISTEN or session-level locks.

In an isolated environment, test peak load and overlapping versions during a deployment. Compare connection wait time, error counts and the latency of the user operation. After a production change, monitor the same measures and prepare a configuration rollback. A lower connection count alone does not show whether clients receive results within an acceptable time.

  • Distinguish pool waiting from database rejection.
  • Include every process and deployment overlap.
  • Check client release and transactions left open for a long time.
  • Measure errors and latency after the configuration change.

What to take away

Size application pools as a shared database connection budget. When capacity runs out, identify the processes and operations holding connections, then test the change under the expected concurrency.

Documentation and further reading

Mgr. Martin Hlavaj, MBA

Software Engineer

All articles

Hear about an outage early.

Add your website or API to UpBot and choose who receives the alert.

Start monitoring for free