Evidence & benchmarks
Yes — and you don’t have to take our word for it. Every number below comes from a committed script you can run yourself (reproduce it). Latest run: 2026-09-08, fresh PostgreSQL 17 container, raw EXPLAIN plans and stage JSONs in the repo.
The wins, one finding at a time
Missing index: 12× faster
3.57 ms → 0.30 ms (−92%)
The query seq-scanned 50,000 rows to find 100. One
CREATE INDEX CONCURRENTLY — the exact remediation pgdba printed — turns it
into an index scan.
Why you can trust it: six control queries (lookups, PK hits, small aggregations) ran in every stage and stayed within noise (0.4–0.7 ms). The win is the index, not the machine.
work_mem too small: 31% faster aggregations
391 ms → 269 ms (−31%)
A 1M-row, 100k-group hash aggregation was spilling to disk batches at the
4 MB default. Raising work_mem to 64 MB — pgdba’s proposal, with the
temp-file evidence cited — keeps the whole hash in memory (Batches: 1 in
the EXPLAIN).
Bloat: 39% of the cost recovered
454 ms → 279 ms (−39%)
With autovacuum disabled and the table churned to 43% dead tuples, the same
aggregation paid for 1.75M tuples instead of 1M. VACUUM (ANALYZE) — the
remediation pgdba prints — removed the dead tuples and the cost came back
down.
Wasteful column types: half the disk, a third faster
24.7 MB → 11.3 MB table (−54%), aggregation 12.1 → 8.4 ms (−31%)
CHAR(1) used as a boolean, NUMERIC(12,0) used as an integer,
UUIDs stored as TEXT, JSON used where JSONB fits. The rewrite is the
table pgdba’s column checks recommend — same rows, same indexes.
Warning
The benchmark dataset fits in page cache: these are plan-shape and CPU wins, not I/O wins. On a larger-than-RAM workload the cache-sizing story dominates instead. Full caveats in RESULTS.md.
Migration: measured too
330 MB / 1M rows, default engine, default flags: 6.9 s end-to-end (~48 MB/s) with preflight, copy, and exact row-count verification. Details and what that number does and does not mean: Migration speed.
What every check actually says
The check examples gallery shows real captured output from all {{ (index hugo.Data “check-examples”).checks_total }} checks against a deliberately messy database — no hand-written examples.
Receipts that don’t need a benchmark
- 68 read-only checks — the full registry
- 1,101 tests on the default branch; 89.6% coverage, enforced in CI
- A sample report with
evidence_quality: fullcommitted in-repo - Every session runs under the safety contract — read-only, timeout, version gate
- Landing-page numbers are pulled from the registries at build time, so “68 checks” cannot silently drift
Reproduce it
# Check-advice wins (container, seeding, 5 stages, column rewrite):
uv run python devdb/bench_check_stages.py
# Migration speed (testbench, 1M rows, default engine):
uv run python devdb/bench_migration.py
Both scripts print their numbers, write JSON, and clean up after
themselves. The per-stage JSONs (with EXPLAIN plans) land in
devdb/results/check-fresh/.
- Migration speed — How fast pgdba migrate moves a database, measured — and how to read the number.
- What every check says — Real captured output from all 68 checks against a deliberately messy database.