On Fri, Jun 21, 2024 at 4:22 PM Melanie Plageman <melanieplage...@gmail.com> wrote: > > While investigating a bug over on [1], I found that > vacuum_set_xid_limits() is calculating freezeLimit in an unsafe way on > at least Postgres 14 and 15. > > limit = *oldestXmin - freezemin; > safeLimit = ReadNextTransactionId() - autovacuum_freeze_max_age; > if (TransactionIdPrecedes(limit, safeLimit)) > limit = *oldestXmin; > *freezeLimit = limit; > > All of these are unsigned, so it doesn't work very nicely when > freezemin (based on autovacuum_freeze_min_age) is larger than > oldestXmin and autovacuum_freeze_max_age is bigger than the next > transaction ID -- which is pretty common right after initdb, for > example. > > I noticed the effect of this because FreezeLimit is saved in the > LVRelState and passed to heap_prepare_freeze_tuple() as cutoff_xid, > which is used to guard against freezing tuples that shouldn't be > frozen. > > I didn't propose a fix because I just want to make sure I'm not > missing something first.
Hmm. So perhaps this subtraction results in the desired behavior for freeze limit -- but by using FreezeLimit as the cutoff_xid for heap_prepare_freeze_tuple(), you can still end up considering freezing tuples with xmax older than OldestXmin. This results in erroring out with "cannot freeze committed xmax" on 16 and master but not erroring out like this in 14 and 15 for the same tuple and cutoff values. - Melanie