Scenario · Vacuum & Bloat
Index bloat after churn
A sandboxed PostgreSQL incident — investigate with your own tools, submit a fix, and get deterministic Detect / Fix / Trap scoring.
L3 · 10–15 min · runs locally in Docker
Launch
Start this scenario
Boot it in a real PostgreSQL sandbox and investigate with psql, EXPLAIN and pg_stat_statements.
ride postgres start stage-07/03-index-bloat-after-churnPart of these paths
Show the postmortem & investigation hints spoilers
Index bloat after churn Type: incident simulation · Topic: Vacuum & Bloat · Level: L3 · Duration: 10–15 min Launch: ride postgres start stage-07/03-index-bloat-after-churn POSTMORTEM (root cause · how it was found · the fix · lesson) Root cause: heavy churn on the indexed column (external_id) in tenant_b.orders left the index on external_id bloated — many dead/!near-empty pages it never gave back — so it grew large and scans over it slowed. How it was found: pg_stat_user_indexes showed the external_id index far larger than its live row count justified, distinct from table-level bloat. The mitigation: rebuild the index — REINDEX INDEX idx_orders_external_id (in production, REINDEX INDEX CONCURRENTLY to avoid blocking writes). Lesson: index bloat needs a rebuild, not just a VACUUM — a plain VACUUM reclaims index entries for reuse but doesn't shrink the index file. Prefer REINDEX CONCURRENTLY (or pg_repack). Dropping the index, adding a redundant one, or reindexing the wrong database is wrong. INVESTIGATION HINTS (the staged path to diagnose and fix) 1. tenant_b's queries slowed and its index is huge after churn on the indexed column. Look at index sizes: \connect tenant_b then SELECT indexrelname, pg_size_pretty(pg_relation_size(indexrelid)), idx_scan FROM pg_stat_user_indexes WHERE relname='orders' ORDER BY pg_relation_size(indexrelid) DESC; 2. This is index bloat: the index on external_id is bloated from heavy update/delete churn. A plain VACUUM reclaims index entries for reuse but does NOT shrink the index file — you need to rebuild it. 3. Rebuild the index in tenant_b: REINDEX INDEX idx_orders_external_id; (in production prefer REINDEX INDEX CONCURRENTLY). Don't add another redundant index, don't drop it, and don't reindex the wrong database.