On Tue, Aug 25, 2026 at 10:06 AM Melanie Plageman <[email protected]> wrote: > I think this proposal (in the email) should explain why freezing xids > and mxids is safe with only an exclusive lock. AFAICT that 2011 thread > doesn't get specific.
As far as I know, the only fundamental reason we require a cleanup lock is to make page defragmentation safe. Pruning as currently defined implies page defragmentation, and freezing is closely tied to pruning, so we tend to treat both as requiring a cleanup lock. But a cleanup lock is likely not strictly necessary. Freezing expects pruning to remove dead tuples, because we have no way to freeze them. But it doesn't actually require page defragmentation. So it is probably possible to invent a variant of pruning that doesn't remove tuple storage, and thus has no need to defragment the page. I'm thinking of an approach that performs an in-place update of xmin and xmax, setting both to InvalidTransactionId. That would enable repurposing lazy_scan_noprune into (say) lazy_scan_nodefrag, which would always be able to freeze (and psuedo-prune) every page without waiting for a cleanup lock. Obviously I haven't prototyped this, so there might be some hard to foresee difficulties. It's possible that pruning relies on cleanup locks in a way nobody realizes right now -- it wouldn't be the first time something like that happened. When I made the second pass over the heap not require a cleanup lock for Postgres 14, it exposed race conditions with how we set the visibility map in that pass. Technically that wasn't my fault; the fixes for those bugs were in and around heap_update IIRC. -- Peter Geoghegan
