Track table and index bloat over time
pg_stat_user_tables exposes dead-tuple counts, so you can compute a bloat percentage and watch it. Here a table sits at 75% dead before a vacuum and 0% after, while the file size stays the same, proving VACUUM reclaims in place.
Problem
What you're actually looking at
The symptom as it shows up on a real server.
Bloat is invisible until it hurts. By turning n_dead_tup and n_live_tup into a percentage you get an early-warning signal, and by watching the size before and after vacuum you learn what vacuum actually does to disk usage.
Meridian tracks courier_positions, which has churned to 75% dead tuples. A vacuum clears the dead tuples but, as expected, does not shrink the file, exactly the distinction operators need to understand.
Simple terms
Bloat is all the dead row versions that updates and deletes leave behind, and it stays invisible until it starts to hurt. PostgreSQL already counts live and dead rows per table, so turning those into a simple "percent dead" gives you an early-warning gauge to watch over time. Checking the table's size before and after a vacuum also teaches the key lesson: a plain VACUUM clears the dead rows but does not shrink the file.
Full runbook for this incident
- The full identify checklist, the exact signals that tell you it's this incident
- Every diagnostic query; lab output is attached only to the steps we actually captured
- The resolution path and the pitfalls that make it worse
- Mitigation steps to stop it recurring, plus a verify-you're-done query
Card required. Cancel before day 7 and you are not charged.
More in this category
Other Observability runbooks
Neighbouring incidents that share the same diagnostic surface.
Need the full procedure?
Pro runbooks finish the incident path
Free runbooks teach the shape. Pro opens the full step transcript, edge cases, and prevention depth.