Skip to main content
Metrics type: Key MetricsCategory: Performance

At a glance

The 99th-percentile query execution time, in milliseconds, across all statements running on the monitored PostgreSQL instance. p99 is the tail: 99 out of every 100 queries finished faster than this number, and 1 in 100 finished slower. For a DBA or SRE this is the “worst realistic query a real user hits” line. p50 tells you the typical experience; p99 tells you what your slowest customers, your heaviest reports, and your most contended checkout writes actually feel. When p99 climbs while p50 stays flat, you have a tail problem (lock contention, a cold cache, a missing index on one hot path), not a system-wide slowdown.

Calculation

The card reads from pg_stat_statements, which records cumulative timing per normalised statement since the last pg_stat_statements_reset() (or instance start). Vortex IQ does not trust the lifetime cumulative figure for a real-time tail metric, because a statement that was slow once at 03:00 would pollute the headline all day. Instead the engine samples the view each interval, computes the delta in calls and total_exec_time since the previous sample, and reconstructs a per-statement mean for the window. The 99th percentile is then taken across the call-weighted distribution: statements that ran 50,000 times in the window count 50,000 times toward the percentile, a statement that ran twice counts twice. In plain terms:
Two engineering notes that affect the reading:
  1. Mean-of-means is an approximation of the true tail. pg_stat_statements stores a mean per statement, not a full histogram, so the p99 is the 99th percentile of statement means, not the 99th percentile of every individual execution. On a workload dominated by a few statement shapes this is very close to the true p99; on a workload with one statement whose individual runs vary wildly, the true tail can be worse than reported. Where the managed-service provider exposes a real latency histogram (Aurora Performance Insights, Cloud SQL Query Insights), Vortex IQ prefers that source and notes it on the card.
  2. Resets reset the baseline. If someone runs pg_stat_statements_reset() mid-window the delta goes briefly negative; the engine detects the counter rollback and skips that one sample rather than reporting a nonsense spike.

Worked example

A B2B SaaS platform runs its primary on a managed PostgreSQL 15 instance (db.r6g.2xlarge equivalent, max_connections = 400). The application is healthy at p50 but the on-call SRE gets a Nerve Centre alert at 14:20 on 11 May 26: p99 query latency has climbed from a steady 180ms to 640ms and held there for six minutes. The SRE pulls the snapshot: The fact that p50 is flat while p99 quadrupled is the whole story. This is not a CPU-saturation event (that would lift the whole curve, p50 included). It is a tail event: a small subset of queries got very slow. The SRE drills into Top 10 Slowest Queries and finds a single UPDATE invoices SET status = $1 WHERE account_id = $2 statement that normally runs in 4ms now averaging 380ms, and accounting for the spike.
The fix was to chunk the batch job into 500-row commits instead of one giant transaction. p99 dropped back to 190ms within two minutes of the long transaction committing. Three takeaways the team recorded:
  1. Read p99 against p50, never alone. p99 of 640ms is alarming on its own, but the diagnostic value is the gap to p50. A wide p50-to-p99 gap means tail contention; a narrow gap that is simply high everywhere means the instance is undersized or starved.
  2. Lock waits count as execution time. A query blocked on a row lock is still “executing” from pg_stat_statements’ point of view. That is why a contention event surfaces here and not on the pool-saturation card. Pair with Idle-in-Transaction Backends when p99 spikes, because a forgotten open transaction is the classic cause.
  3. A 500ms p99 is a budget, not a verdict. For an interactive OLTP front end, 500ms at the tail is roughly where users start to feel sluggishness. For an analytics or reporting database, a 500ms p99 may be entirely fine. Tune the alert threshold to your workload in the Alert Rules tab rather than treating the default as gospel.

Sibling cards to read alongside

Reconciling against the source

Where to look in PostgreSQL’s own tooling:
pg_stat_statements is the authoritative source. Order by mean to see where the tail lives:
pg_stat_activity shows what is running right now and what each backend is waiting on (wait_event_type, wait_event), which tells you whether the tail is lock waits or I/O. EXPLAIN (ANALYZE, BUFFERS) on the suspect statement gives the real plan and the actual time.
On managed services:
Amazon RDS / Aurora: Performance Insights exposes per-statement latency and wait-event breakdowns directly, and is the closest native equivalent to this card’s percentile. Google Cloud SQL: Query Insights provides per-query latency and plan sampling. Azure Database for PostgreSQL: Query Store and the Intelligent Performance blade hold the equivalent statement-level timings.
Why our number may legitimately differ from a raw pg_stat_statements read: Cross-connector reconciliation:

Known limitations / FAQs

The card shows a high p99 but every query I run by hand is fast. Why? Three usual causes. First, the tail is intermittent: the slow runs happen under concurrency you cannot reproduce with a single manual query, typically lock contention from another session. Second, your manual run hits a warm cache while the slow production runs hit cold pages (check Buffer Cache Hit Rate %). Third, the slow statement is a different shape than the one you tested; run the pg_stat_statements query above ordered by mean_exec_time to find the real culprit. Is p99 the right tail to watch, or should I use p95 or max? p99 is the standard SLO tail for interactive workloads: it captures genuine pain without being dominated by single freak outliers the way max_exec_time is. p95 is more forgiving and good for “most users”; max is too noisy to alert on. Watch p99 for alerting and keep p50, p95 visible for context. The trio together tells you the shape of the curve. Why does my Datadog or New Relic APM p99 differ from this card? APM measures latency from the application’s point of view: it includes time waiting for a connection from the pool and network transit to the database. This card measures only the time PostgreSQL spent executing the statement. APM p99 is therefore almost always higher. A large gap between the two points at pool or network problems rather than query problems. pg_stat_statements is not installed. Does this card work? No, this card requires the pg_stat_statements extension (it ships with PostgreSQL but must be added to shared_preload_libraries and created with CREATE EXTENSION pg_stat_statements). On managed services it is usually available as a parameter-group toggle. Without it, Vortex IQ falls back to the provider’s native latency metric (Performance Insights, Query Insights) where present, and otherwise the card is unavailable. The connector setup screen flags this during onboarding. Does p99 include time the query spent waiting for a lock? Yes. From pg_stat_statements’ perspective a statement blocked on a row or table lock is still executing, so lock-wait time is included in execution time and therefore in p99. This is intentional and useful: it means contention shows up here. Pair with Idle-in-Transaction Backends and Deadlocks (last 5m) to confirm contention as the cause. The default 500ms alert is too sensitive (or not sensitive enough) for my workload. Adjust it in the Alert Rules tab per profile. An interactive OLTP front end might want 250ms; a reporting or analytics database might be perfectly healthy at a 2,000ms p99. The 500ms default is a reasonable OLTP starting point, not a universal truth. Set the threshold against your own latency budget. Can a single very slow query move the p99, or does it take many? Because the percentile is call-weighted, one statement that runs rarely will barely move p99 even if each run is slow. To move p99 you need a statement shape that runs often enough to occupy the top 1% of the call-weighted distribution. That is by design: it stops a single nightly maintenance query from triggering false alarms while still catching a hot path that has regressed.

Tracked live in Vortex IQ Nerve Centre

Query Latency p99 (ms) is one of hundreds of KPI pulses Vortex IQ tracks across PostgreSQL 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.