Developers see what their code asks
Every statement a service sends, normalized, with its plan, what it waited on, and the runs behind it down to the application and client that sent each one.
Find the statement, the lock or the config change behind a slow database, all in one view.
14 days, all features, no credit card required.
Last 1h
Total time over calls
of pg_stat_statements.max 5,000
Last 1h, all statements
| Statement L | Calls L | Total L | Mean L | Rows / call L | Share of time L | Plan L |
|---|---|---|---|---|---|---|
SELECT * FROM orders WHERE customer_id = $1 AND status = $2 ORDER BY created_at DESC LIMIT $3 | 1,204,331 | 2,890 s | 2.4 ms | 20 | Index Scan | |
UPDATE ledger_entries SET balance = balance + $1 WHERE account_id = $2 | 384,120 | 2,343 s | 6.1 ms | 1 | Index Scan | |
SELECT p.id, p.amount, r.amount AS refunded FROM payments p JOIN refunds r ON r.payment_id = p.id | 2,412 | 1,958 s | 812 ms | 500 | Seq Scan | |
INSERT INTO audit_log (actor_id, action, payload, created_at) VALUES ($1, $2, $3, $4) | 2,100,000 | 1,260 s | 0.60 ms | 1 | Insert | |
SELECT count(*) FROM sessions WHERE expires_at < now() | 1,440 | 922 s | 640 ms | 1 | Seq Scan | |
DELETE FROM idempotency_keys WHERE created_at < $1 | 96 | 404 s | 4.21 s | 18,400 | Index Scan |
SELECT * FROM orders WHERE customer_id = $1 AND status = $2 ORDER BY created_at DESC LIMIT $3Last 1h
Total time over calls
Reads match returns
2,890 s of 13,940 s
Limitactual 2.1 msrows 20 est. rows 20
Index Scan using orders_customer_status_idx on ordersactual 2.0 msrows 20 loops 1
(customer_id = $1) AND (status = $2)Buffers: shared hit=24 read=2CPU2,081 s72%QueryIO:DataFileRead607 s21%QueryLWLock:BufferMapping202 s7%Query| Time | Duration L | Rows L | Application | Client | Parameters L | Source |
|---|---|---|---|---|---|---|
| 14:51:07.212 | 5.8 ms | 22 | payments-api | 10.4.9.18 | $1 = | Full |
| 14:48:55.904 | 4.1 ms | 18 | payments-api | 10.4.12.31 | $1 = | Full |
| 14:44:12.630 | 3.0 ms | 20 | payments-api | 10.4.9.22 | $1 = | Full |
| 14:39:40.071 | 2.5 ms | 20 | payments-api | 10.4.9.18 | $1 = | Full |
| 14:31:03.558 | 2.0 ms | 19 | payments-api | 10.4.12.31 | $1 = | Full |
| 14:27:48.117 | 1.9 ms | 7 | payments-api | 10.4.9.22 | $1 = | Full |
| 14:22:15.483 | 1.7 ms | 20 | payments-api | 10.4.12.31 | $1 = | Full |
Ceiling = max_locks_per_transaction 16 x max_connections 400
Behind 1 holder
Lock:tuple on ledger_entries
Last 1h
pid 48121app_paymentsRowExclusiveLockledger_entriesidle in transaction 14 sQueryblocks 3
pid 48210app_paymentswaiting Lock:tuple14 spid 48233app_paymentswaiting Lock:tuple11 spid 48302ledger-syncwaiting Lock:tuple6 sHolder last ran: UPDATE ledger_entries SET balance = balance + $1 WHERE account_id = $2
| Holder L | User | Lock mode | Relation | Max blocked L | Longest wait L | First seen | Last seen | Snapshots L |
|---|---|---|---|---|---|---|---|---|
pid 48121 | app_payments | RowExclusiveLock | ledger_entries | 3 | 14 s | 14:51:02 | 14:51:16, ongoing | 2 |
pid 47960 | ledger-sync | ShareLock | ledger_entries | 2 | 8 s | 14:44:20 | 14:44:30 | 2 |
pid 47712 | app_payments | AccessExclusiveLock | payments | 6 | 41 s | 14:39:00 | 14:39:40 | 5 |
database = payments
Tables and indexes
8.0 GB, 0 scans in 30d
No statement references them in 30d
| Table | Rows M | Size M | Seq scans M | Index reads M | Share of I/O M | Unqueried columns D |
|---|---|---|---|---|---|---|
payments | 4.1M | 3.8 GB | 3,852 | 1.2M | 2 | |
orders | 12.6M | 9.1 GB | 12 | 1.2M | 0 | |
ledger_entries | 48.2M | 22.4 GB | 0 | 384K | 1 | |
audit_log | 210M | 61.0 GB | 0 | 0 | 4 | |
sessions | 880K | 0.4 GB | 1,440 | 0 | 0 | |
refunds | 220K | 0.1 GB | 0 | 2,412 | 0 |
| Column | Type | Statements D | Status |
|---|---|---|---|
id | uuid | 412 | Primary key |
customer_id | uuid | 130 | Indexed |
amount | numeric(12,2) | 96 | |
status | text | 88 | |
created_at | timestamptz | 54 | Filtered, not indexed |
legacy_gateway_ref | text | 0 | Never queried |
gateway_response | jsonb | 0 | Never queried |
id | uuid | 220 | Primary key |
customer_id | uuid | 164 | Indexed |
status | text | 151 | Indexed |
created_at | timestamptz | 143 | |
total | numeric(12,2) | 72 | |
currency | char(3) | 41 | |
shipping_address | jsonb | 18 | |
id | bigint | 96 | Primary key |
account_id | uuid | 210 | Indexed |
balance | numeric(14,2) | 184 | |
entry_type | text | 61 | |
created_at | timestamptz | 77 | |
payment_id | uuid | 33 | |
memo | text | 0 | Never queried |
id | bigint | 3 | Primary key |
actor_id | uuid | 2 | Indexed, never read |
action | text | 2 | |
payload | jsonb | 1 | |
created_at | timestamptz | 2 | |
ip_address | inet | 0 | Never queried |
user_agent | text | 0 | Never queried |
request_id | uuid | 0 | Never queried |
session_ref | text | 0 | Never queried |
id | uuid | 58 | Primary key |
user_id | uuid | 44 | Indexed |
expires_at | timestamptz | 6 | Filtered, not indexed |
data | jsonb | 12 | |
created_at | timestamptz | 9 | |
id | uuid | 24 | Primary key |
payment_id | uuid | 19 | Indexed |
amount | numeric(12,2) | 17 | |
reason | text | 8 | |
created_at | timestamptz | 11 |
| Index | Size M | Reads M | Status |
|---|---|---|---|
payments_pkey | 210 MB | 1.2M | |
payments_customer_id_idx | 190 MB | 380K | |
orders_customer_status_idx | 640 MB | 1.2M | |
ledger_entries_account_id_idx | 1.1 GB | 384K | |
refunds_payment_id_idx | 12 MB | 2,412 | |
payments_legacy_gateway_ref_idx | 1.8 GB | 0 | Unused |
audit_log_actor_idx | 6.2 GB | 0 | Unused |
# RDS parameter group dbm-payments-pg16# RDS parameter group dbm-payments-pg16shared_preload_libraries = 'pg_stat_statements,auto_explain'shared_preload_libraries = 'pg_stat_statements,auto_explain'shared_buffers = 16GBshared_buffers = 16GBmax_connections = 400max_connections = 400max_locks_per_transaction = 64max_locks_per_transaction = 16work_mem = 16MBwork_mem = 16MBtrack_io_timing = ontrack_io_timing = onlog_min_duration_statement = 500mslog_min_duration_statement = 500msauto_explain.log_format = jsonauto_explain.log_format = jsonauto_explain.log_analyze = onauto_explain.log_analyze = onauto_explain.log_timing = offauto_explain.log_timing = offauto_explain.sample_rate = 0.01auto_explain.sample_rate = 0.01statement_timeout = 0statement_timeout = 30sEvery statement a service sends, normalized, with its plan, what it waited on, and the runs behind it down to the application and client that sent each one.
Top statements, blocking chains, unused indexes and parameter drift on one page per instance, each number marked with where it came from.
A light baseline on every instance, sampled statements where it matters, and full capture for one role for an hour, switched off on its own.
Literal values are folded into $1, $2, so a million runs of a statement add up to one row: calls, total and mean time, rows per call, its share of the instance’s time and the plan it ran. Sequential scans stand out before they turn into an incident.
Open a statement to see the plan it actually ran, what it waited on, and every run with its duration, rows, application and client. With Full collection on, each run keeps its literal parameters, so you can replay the exact query.
A live blocking tree shows the session holding the lock, every session queued behind it and the statement it last ran, refreshed every 10 seconds. Each blocking episode in the range is kept, so the one at 3 a.m. is still there in the morning.
Tables ranked by their share of I/O, indexes with no reads in 30 days, and columns no statement has touched. Drop the dead weight, and add the index a filtered column is missing.
Every parameter change lands on the instance’s health timeline, and any two versions compare side by side. Here max_locks_per_transaction dropped from 64 to 16 on Nov 1, and the instance turned Degraded the next day.
Detail is a switch, not a contract. Every instance starts light, and you turn up what you collect for the scope and the time you choose.
Health and instance metrics for every database you run: connections, throughput, replication, storage and the configuration timeline.
For every instance, all the timeNormalized statements with their timing, a sample of runs, explain plans, wait events, locks and schema usage.
For the databases you care aboutEvery run with its literal values, scoped to a role, an application or a database, and switched back to Sampled when the time runs out.
For one scope, for an hourFollow a slow request from the service that sent it to the statement that held it up.
Learn moreSee the host, the volume and the network under every instance, in the same explorer.
Learn moreRead the database’s own logs, from slow-statement lines to deadlock reports, next to its metrics.
Learn moreAsk why the database is slow in plain words, and get the statement, the lock and the change.
Learn moreConnect a database and see its top statements, locks and indexes in minutes.
14 days, all features, no credit card required.