Skip to main content
Metrics type: Key MetricsCategory: Cross-Platform: Revenue at Risk

At a glance

Connection-pool saturation on the MySQL instance, plotted against the storefront traffic burst happening at the same moment. Saturation is Threads_connected / max_connections. The card asks one question: when traffic surged, did the database run out of connection headroom? If saturation crosses 90% during a burst, the next wave of shoppers gets ER_CON_COUNT_ERROR (“Too many connections”) and their page loads or checkouts fail. This is the database-side early warning for a traffic-driven outage, correlated with the ecom connector’s live session and order rate so a platform team can see cause and effect on one chart.

Calculation

The MySQL side is a straight ratio. Threads_connected (the count of currently open client connections, threads in use plus idle-but-held) is read from SHOW GLOBAL STATUS and divided by the max_connections server variable:
For example, 540 connected threads against a max_connections of 600 is 90% saturation. The card also surfaces Max_used_connections (the high-water mark since start) so you can see how close the instance has ever come to the ceiling. The traffic side comes from the linked ecom connector’s live concurrency signal (active sessions or request rate). The card joins the two series on a shared 15-minute rolling window, sampled in real time, and emits one row per interval so the correlation is visible row by row rather than as two disconnected charts. The alert fires only on the conjunction: saturation above 90% AND a concurrent traffic burst. This is deliberate. Saturation at 90% during a quiet hour is a connection leak or a runaway batch job, a different problem handled by the standalone Connection Pool Saturation % card. This cross-platform card is specifically about traffic-driven exhaustion, the kind that turns a marketing-driven surge into a wave of failed checkouts.

Worked example

A platform team runs the storefront on a single primary MySQL instance with max_connections = 600 and a PHP-FPM application tier behind it. On 14 Apr 26 at 19:00 BST an email campaign and a paid-social push land within minutes of each other. Snapshot of the 15-minute rolling window: At 19:07 the card breaches the >90% during traffic burst alert. At 19:09 the pool is fully exhausted: new connection attempts return ER_CON_COUNT_ERROR (1040): Too many connections. The application tier starts throwing 500s, and shoppers mid-checkout see an error page.
What the platform team learns from the correlation: saturation tracked traffic almost linearly up to 88%, then the last 1,200 sessions pushed it over the edge. The fix is not just “raise max_connections” (that risks OOM if each connection’s per-thread buffers add up beyond available RAM). The durable fix is a connection pooler (ProxySQL or the application framework’s persistent-pool settings) so that 7,400 sessions multiplex over a far smaller number of backend connections, plus right-sizing max_connections against measured per-connection memory. Pair with Memory Usage % before raising the ceiling. Three takeaways:
  1. The conjunction is the signal. Saturation alone is ambiguous; saturation during a known traffic burst is a clear, actionable, revenue-linked event.
  2. Linear is fine, super-linear is not. If saturation rises faster than traffic, connections are being held open too long (slow queries, missing pooling, leaked handles). Pair with Query Latency p95 (ms).
  3. The ceiling is not free to raise. Each connection costs memory; raising max_connections without checking RAM headroom trades a connection error for an OOM-kill, which is worse.

Sibling cards

Reconciling against the source

Where to look in MySQL directly:
SHOW GLOBAL STATUS LIKE 'Threads_connected'; for the live connection count. SHOW GLOBAL STATUS LIKE 'Max_used_connections'; for the high-water mark since start. SHOW VARIABLES LIKE 'max_connections'; for the ceiling. SELECT * FROM performance_schema.processlist; (or SHOW PROCESSLIST) to see who holds each connection. SHOW GLOBAL STATUS LIKE 'Connection_errors_max_connections'; counts attempts rejected because the pool was full.
On managed services, RDS and Aurora expose DatabaseConnections in CloudWatch (compare against the max_connections derived from the instance class), and Cloud SQL exposes connection count in Cloud Monitoring. Performance Insights and Query Insights both chart connection counts over the burst window. Why our number may legitimately differ from the native tooling: Cross-connector reconciliation:

Known limitations / FAQs

Saturation hit 95% but no shoppers reported errors. Why? You had headroom in the application tier’s own pooling, or the burst was brief enough that connections were recycled before the ceiling was hit. Saturation at 95% is a warning, not a guaranteed outage. The outage happens at 100%, when max_connections is fully consumed and the next attempt gets error 1040. Treat the 90% breach as the moment to act, not the moment shoppers feel it. Should I just raise max_connections to stop this happening? Cautiously. Each connection reserves per-thread memory (sort buffers, join buffers, read buffers). On an instance with thousands of connections this adds up fast and can trigger an OOM-kill, which takes the whole database down rather than refusing one connection. Check Memory Usage % first, and prefer a connection pooler (ProxySQL, or your framework’s persistent pool) so the application multiplexes many sessions over few backend connections. Why correlate with traffic at all instead of just alerting on saturation? Because the same 90% reading means very different things at different times. At peak traffic it is a capacity problem you may need to fix with pooling or scaling. At 3am it is a connection leak or a runaway batch job. The traffic overlay tells you which conversation to have, and lets you size the revenue impact directly against shopper concurrency. The chart shows saturation rising faster than traffic. What does that mean? Connections are being held open longer than they should be. The usual causes: slow queries occupying a connection for seconds instead of milliseconds (check Query Latency p95 (ms)), missing connection pooling so each request opens a fresh connection, or leaked handles the application never closes. Fixing query latency or adding pooling flattens the curve. Does this work on a read-replica topology? The card reads the instance it is connected to. If reads are routed to replicas and writes to the primary, point the connector at the node that actually receives shopper traffic, usually the primary for checkout writes. Saturation on a replica that only serves a reporting tool is not shopper-facing and should not be confused with checkout-path saturation. On RDS / Aurora the max_connections value looks like a formula, not a number. RDS derives max_connections from a parameter-group formula based on instance memory (for example {DBInstanceClassMemory/12582880}). The card reads the resolved runtime value via SHOW VARIABLES, so the denominator is the actual effective ceiling, not the formula text. If you change the instance class, the ceiling changes and so does the saturation percentage.

Tracked live in Vortex IQ Nerve Centre

MySQL Pool Saturation vs Traffic Burst is one of hundreds of KPI pulses Vortex IQ tracks across MySQL and 70+ other ecommerce connectors. Nerve Centre runs the detection layer; Vortex Mind investigates the cause when something moves; Ask Viq lets you interrogate any number in plain English. Start for free or book a demo to see this metric running on your own data.