Build Your Own PostgreSQL
Vacuum and the Cost of History
What You Will Learn
- Explain the visibility proof required before reclaiming a dead tuple version
- Trace vacuum across heap space, index entries, visibility metadata, and freezing
- Connect long transactions and autovacuum delay to bloat and wraparound risk
History has weight
A bookstore deletes a cancelled order, yet an older report may still be entitled to see its previous tuple version. Erasing that version at commit would break the report; retaining every version forever would fill storage and eventually make transaction identifiers ambiguous. Vacuum separates logical death from physical reclamation and performs the proof that bridges them.
A tuple becomes dead when no possible snapshot can still see it. Proving that requires knowledge of active transactions. If a long-running transaction began before a delete committed, it may still need the old version. Vacuum must respect that reader even if every newer transaction would ignore the tuple. The process array, transaction horizons, and visibility rules all feed into the cleanup decision.
Cleanup has to prove safety
When vacuum scans a heap page, it identifies dead tuple versions, marks their space reusable, and may update page-level metadata. It also coordinates with indexes. An index may contain entries pointing to dead heap tuples, so those entries need removal or marking depending on the index method. Vacuum is more than a heap cleaner. It reconciles several structures in one pass.
Cleanup loop
Trace the proof before reuse
Vacuum derives a horizon from transactions and snapshots that can still affect visibility. It scans a heap version whose deleting transaction committed before that horizon. Because no possible relevant snapshot can still accept the version, vacuum can arrange removal of index entries that route to it and mark the heap space reusable. The bytes are reclaimed only after the visibility claim has become universally false.
During the same maintenance work, PostgreSQL may record page-level all-visible or all-frozen facts and replace sufficiently old transaction metadata with a form safe from wraparound interpretation. These are correctness claims, not mere cache hints. A mistaken reusable or all-visible claim could destroy history or expose an invisible tuple, so cleanup must be conservative when proof is unavailable.
Immediate erasure would break readers
Immediate cleanup sounds attractive until a concurrent reader still needs the old version. If the database removed old versions as soon as an update or delete committed, the writer would have to wait for every possible old reader, which destroys much of MVCC's advantage. Vacuum decouples logical deletion from physical reclamation. Writers move on, and cleanup happens once the system proves it is safe.
Lazy cleanup fails in the other direction. Dead tuples consume space, make scans slower, and leave indexes full of entries that lead nowhere useful. There is also the problem of transaction identifiers wrapping around. Since tuple visibility depends on comparing transaction ages, the system must prevent old IDs from being mistaken for new ones. Freezing marks old tuples in a way that no longer depends on their original transaction ID. Vacuum is the path to that safety.
The background worker
Autovacuum exists because humans cannot reliably schedule this by hand. Workloads vary by table, hour, and deployment. Some tables are append-only and need little cleanup. Others churn constantly. The database monitors update and delete activity and launches vacuum workers when thresholds are crossed. The goal is to clean aggressively enough to control bloat and wraparound risk without taking too much I/O and CPU from foreground queries.
Bloat is what history leaves behind in the open. A table can have far more allocated pages than live rows require because dead space is scattered or not yet reusable. Indexes can bloat when deleted entries leave pages sparse but not easily merged. Vacuum can reuse space within existing files, but it often does not shrink files back to the operating system. Rewriting or rebuilding may be needed for severe cases. The architectural point is that deletion in an MVCC database is a process rather than a single moment.
Visibility maps help vacuum and index-only scans. A page marked all-visible contains tuples visible to every transaction, so some operations can skip per-tuple checks. A page marked all-frozen has transaction metadata old enough to be safe from wraparound concerns. These maps turn repeated global questions into page-level facts. They must be maintained carefully because a wrong all-visible mark could let a query return data it should not see.
Vacuum also has to be careful around locks. It should not block ordinary reads and writes for long periods. It may skip pages or relations when stronger locks are unavailable, then return later. Some forms of vacuum are lightweight and concurrent. Others, such as full rewrites, require stronger locks and cause more disruption. The system offers multiple cleanup intensities because not every mess justifies that level of disruption.
The best vacuum is the one users rarely notice. It runs in the background, advances horizons, clears dead entries, freezes old tuples, and leaves enough free space for future changes. When it falls behind, the whole database starts to feel heavier. Queries touch more pages. Index scans recheck more dead entries. Storage grows. Emergency wraparound protection can force unpleasant pauses.
Reclamation is a proof, not a timer
Age alone does not make a version removable. Vacuum needs a horizon that excludes every snapshot entitled to see it, a committed outcome that makes it dead, and coordination with structures still referencing it. Operational thresholds decide when to attempt the work; visibility rules decide whether the work is safe. This distinction explains both harmless autovacuum delay and dangerous long-lived horizons.
Into recovery
Vacuum shows that the cost of fast concurrent history is paid later. The next related question is what happens when history is interrupted by a crash. If the server stops midstream, the database needs a known place to begin replay and a way to rebuild pages to a coherent state. That is the work of checkpoints and crash recovery.
Cleanup keeps the live system healthy. Recovery rebuilds the interrupted system into a state you can trust.