pgblame

Supabase query performance: finding and tracking slow queries

Supabase's Query Performance tab reads pg_stat_statements and shows your slowest queries by total time right now. It can't show history or tie a regression to your deploy. Enable pg_stat_statements (it's preinstalled), sort by total time, and add tooling for trends over time.

If you're on Supabase, you already have a Query Performance view in the dashboard (under Advisors → Query Performance). It lists your slowest queries by cumulative execution time, with call counts — and for "what's hammering my database right now?", it's genuinely enough.

It's powered by pg_stat_statements, the Postgres extension Supabase ships preinstalled. Understanding what that view can and can't do is the key to not getting surprised by a slow query in production.

Make sure pg_stat_statements is enabled

Supabase has the extension available but not always toggled on. In the SQL editor (connected as postgres):

CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

Then read the slowest queries directly — this is essentially what the dashboard tab runs:

SELECT
  query,
  calls,
  round(mean_exec_time::numeric, 1) AS avg_ms,
  round(total_exec_time::numeric, 0) AS total_ms
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 20;

Sort by total_exec_time (cumulative cost), not mean_exec_time — a 5ms query called a million times hurts more than a 2s query called twice.

Reading it without a superuser

Don't point monitoring at the postgres superuser. Supabase keeps pg_stat_statements in the extensions schema, so a read-only role needs pg_monitor plus USAGE on that schema:

CREATE ROLE pgblame_reader WITH LOGIN PASSWORD '<choose-something-strong>';
GRANT pg_monitor TO pgblame_reader;        -- includes pg_read_all_stats
GRANT USAGE ON SCHEMA extensions TO pgblame_reader;
GRANT CONNECT ON DATABASE postgres TO pgblame_reader;

(The full walkthrough, including the pooler-host and username gotchas, is in the Supabase setup docs.)

The manual ceiling

The Query Performance tab — and the raw view — only ever show you now. Cumulative totals since the last stats reset, with no history and no link to your deploys (only Supabase's platform events). So when a query that used to be 40ms is suddenly 800ms, the dashboard can tell you it's slow, but not that it changed, when, or which deploy was live. You'd have to have been snapshotting pg_stat_statements yourself, on a schedule, to answer that.

Tracking it over time

That snapshot-over-time job is what pgblame automates. The agent reads pg_stat_statements every 60 seconds and stores per-query deltas; your Vercel or Railway deploys land on the same timeline via a webhook. The result is a "Since last deploy" view that turns "is this slow?" into "this got slow after this deploy."

It's the same data Supabase's tab reads — just kept over time and joined to your releases, on a stack that doesn't have to be Rails (compare pganalyze at 8× the price).

See which deploy slowed your Postgres — free, no card.

Start free — no card

FAQ

Where is Query Performance in Supabase?
Supabase dashboard → Advisors → Query Performance (also reachable from the Reports area). It's backed by the pg_stat_statements extension, which Supabase ships preinstalled.
Does Supabase Query Performance show history?
No. It shows current cumulative stats from pg_stat_statements. There's no built-in trend over time and no link to your application deploys — only Supabase platform events. For 'was this always slow or did my deploy break it?', you need snapshots over time.
What role does pgblame need on Supabase?
A read-only role with pg_monitor (which includes pg_read_all_stats) plus USAGE on the extensions schema, since Supabase keeps pg_stat_statements there. Full SQL is in the Supabase setup docs.
Will monitoring slow my Supabase database?
No meaningfully. Reading pg_stat_statements is one cheap SELECT against a system view; pgblame's agent runs it once every 60 seconds with a short statement timeout.

Related

Supabase query performance: find slow queries