Skip to content

    Back to articles

    6 min read

    A connection-count metric is not a pool health check

    A count answers only one question

    A connection count can tell me how many clients a database sees. It cannot tell me whether an application is waiting for a connection, whether every pooled connection is busy, or whether many connections are simply open and idle.

    Azure Database for PostgreSQL added client_connections_total in its September 2026 release notes. I see that as a useful prompt to ask a more careful question: what does this particular metric count, and what operational decision should it support? The name alone is not enough to settle whether it represents a current count or an accumulated total, how Azure aggregates it, or what dimensions are available. I would confirm those details in the Azure PostgreSQL monitoring documentation before building an alert around it.

    Even with its exact semantics understood, a database-side connection count is only one view of the system. It describes connections visible at the database boundary. The application pool has a different job: it manages requests competing for connections before those requests reach PostgreSQL. A high database count might be harmless when the application has capacity and requests are completing promptly. A modest count might still accompany a bad user experience if a small pool is saturated, or if connections are held for too long.

    I would treat the database metric as context, not as a verdict on pool health. For the verdict, I need signals from the pool and from the requests using it.

    Instrument the application side of the boundary

    The pool knows facts the database cannot infer. It can report how many connections are open, how many are in use or idle, how many callers are waiting, and how long they wait. OpenTelemetry’s database client metric conventions describe connection-state, maximum-connection and pending-request metrics. Those signals answer different questions from a server-side count: not just how many connections exist, but whether application work is competing for them.

    In a .NET application using Npgsql, I would start with the driver’s pool metrics rather than trying to reconstruct pool pressure from PostgreSQL sessions. Npgsql documents connection counts by state, including idle and used connections, along with the pool name, so separate pools can be examined independently in its OpenTelemetry metrics documentation. The exact instrumentation and names depend on the client library; I would check what the deployed driver actually emits instead of assuming that every application exposes the same measurements.

    I would put those pool signals next to request signals: request rate, latency, errors, and traces or spans around database acquisition and execution. That combination helps distinguish a pool wait from a slow query. If acquisition time rises while query execution time remains steady, the application may be queuing before it reaches PostgreSQL. If the database work itself slows, adding pool capacity could make contention worse rather than fix it. That is a diagnosis to validate with the workload, not a conclusion a connection counter can provide.

    Idle connections need their own interpretation. An idle connection is not automatically unhealthy; pools commonly retain connections for reuse. But a rising count of idle database sessions alongside a high client count can point to a different capacity question than a pool with few idle connections and sustained waiters. Npgsql’s connection-string parameter documentation describes settings for maximum pool size and idle-connection lifetime. Those settings express policy; the metrics show whether the policy fits actual demand.

    Build a dashboard around a decision

    I would not put every available counter on one dashboard and call that observability. I would organize the view around the question an operator needs to answer: are requests waiting for a connection, waiting on database work, or simply using a healthy pool with connections available?

    A useful first view pairs the Azure-side client count with application-side pool state and request behavior. The database count helps identify what the server sees. Pool usage and wait signals show whether the application is under pressure. Request latency and errors show whether that pressure matters to callers. When the picture is ambiguous, query and trace details help identify whether the delay is at acquisition, execution, or elsewhere in the request path.

    I would also keep the boundaries visible. A database metric may aggregate connections across more than one application instance or pool; an application metric may describe only one process or pool. Comparing those numbers without aligning scope can produce a confident but wrong story. Use dimensions, labels, and resource identity to line up what is being compared, and document whether the metric is a gauge or a cumulative counter before interpreting its trend. Azure’s monitoring guide describes available dimensions and how metrics can be filtered or split; I would use those capabilities where they match the question, not simply because they exist.

    Alerts should follow the same principle. I would not alert just because a connection count crossed a threshold unless that threshold represents a real server-side limit or operational risk. For application impact, I would alert on sustained pool waits, acquisition timeouts, or request degradation, and use the database count to give responders context. The threshold and duration should come from the service’s capacity and workload, not a number copied from another system.

    Correlate, then choose an action

    When an alert fires, I want the dashboard to narrow the next step. A rising client count with no pool waits and healthy request behavior may call for observation, not a pool change. Waiters and acquisition delays with unused database capacity suggest investigating pool limits or connection lifetimes. A full pool combined with slow database operations suggests examining query behavior and database load before increasing the pool. These are hypotheses to test against traces and workload data, not automatic mappings from one metric to one fix.

    That distinction matters because increasing a pool limit can move pressure rather than remove it. If many application instances each open more connections, the database may receive more concurrent work. Conversely, lowering pool limits to reduce the server count can create longer queues inside the application. I would make that trade-off with measurements from both sides of the boundary.

    A connection count is valuable when I know exactly what it counts and what decision it informs. It becomes misleading when I treat it as a proxy for contention, queueing, or user impact. Pair the new Azure metric with pool and request signals, then design the dashboard to answer an operational question. More counters show more data; the right signals show where work is waiting.

    Drafted with AI assistance, reviewed and edited by Ali.