Skip to content

feat: add postgres instrument to collect the pg_stat_statements dump - #534

Draft
fargito wants to merge 3 commits into
mainfrom
postgres-instrument
Draft

fargito wants to merge 3 commits into
mainfrom
postgres-instrument

Conversation

@fargito

@fargito fargito commented Sep 12, 2026

Copy link
Copy Markdown
Member

Add a postgres instrument that captures each benchmark's SQL, call counts, buffer hits, and query plans from pg_stat_statements, keyed to that benchmark's flamegraph.

Enabled with --instruments postgres --postgres-dsn-env-name <ENV>, where the env var holds a superuser connection string — passed by name, not value, so the DSN never reaches the command line, the config dump, or the uploaded logs (mirroring --mongo-uri-env-name).

The runner observes benchmark boundaries over the instrument-hooks FIFO and, on its own connection, resets pg_stat_statements at each benchmark's start and snapshots + EXPLAIN (GENERIC_PLAN, FORMAT JSON)s at each stop — both in the pre-Ack window, so the SQL runs outside the measured region and never perturbs the walltime/flamegraph measurement. Each snapshot is keyed to the benchmark URI (the same one its flamegraph uses) and written to <profile_folder>/instruments/postgres.json as { benchmarks: [{ uri, queries: [...] }] }, riding along in the uploaded profile archive.

Capture is non-fatal: a connection or query failure is logged and never fails the run. On a URI/snapshot count mismatch the artifact is skipped rather than risk misattributing queries to the wrong benchmark.

Requires Postgres 16+ (for GENERIC_PLAN) and a superuser role; targets walltime runs for now (simulation has no runner-side boundary callback yet).

Add a `postgres` instrument enabled via `--instruments postgres`. Unlike
the MongoDB instrument it owns no process and does no proxying: the
`codspeed/postgres` image runs a poller that writes a pg_stat_statements
analytics dump to a file. This instrument copies that dump (path given by
the required `--postgres-dump-path`) into
`<profile_folder>/instruments/postgres.json` after the run, so it rides
along in the uploaded profile archive.

collect() waits for the poller to flush a dump newer than the run end
before copying, so the final queries aren't dropped by the poller's tick
interval.
@codspeed

codspeed Bot commented Sep 12, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

✅ 17 untouched benchmarks


Comparing postgres-instrument (af05a49) with main (37eba9e)

Open in CodSpeed

Replace the whole-run poller-copy instrument with per-benchmark capture
driven by the runner. On its own connection (--postgres-dsn), reset
pg_stat_statements at each benchmark's StartProfiler and snapshot + EXPLAIN
at StopProfiler, in the pre-Ack window so the SQL stays outside the measured
region. Key each snapshot to the benchmark URI (the same one its flamegraph
uses) and write a per-benchmark artifact instruments/postgres.json =
{benchmarks:[{uri, queries}]}.

Drops --postgres-dump-path and the whole-run collect; the image now only
needs pg_stat_statements preloaded, and the read+EXPLAIN logic lives in the
runner.
Take --postgres-dsn-env-name (the name of an env var holding the DSN) instead
of the DSN itself, mirroring --mongo-uri-env-name. The DSN is resolved at
connect time and never stored in the config, so it can't leak into the config
debug dump, runner.log, or the uploaded archive, nor onto the command line.

Also skip the artifact entirely (rather than zip by index) when the URI and
snapshot counts disagree, so queries are never misattributed to the wrong
benchmark, and keep a DSN-gated isolation test covering the reset-per-boundary
invariant.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant