Hello hackers, In heap_prepare_freeze_tuple, we make the following assumption:
* It is assumed that the caller has checked the tuple with * HeapTupleSatisfiesVacuum() and determined that it is not HEAPTUPLE_DEAD * (else we should be removing the tuple, not freezing it). Thus, when we see a committed xmax that precedes the cutoff_xid, we throw the following data corruption error: errmsg_internal("cannot freeze committed xmax %u", xid) However, in the caller (lazy_scan_heap), HeapTupleSatisfiesVacuum may return HEAPTUPLE_DEAD for an updated/deleted tuple that got modified by a transaction older than OldestXmin. And, if the tuple is HOT-updated, it should only be removed by a hot-chain prune operation. So, we treat the tuple as RECENTLY_DEAD and don't remove the tuple. So, it may lead to an incorrect data corruption error. IIUC, following will be the exact scenario where the error may happen, An updated/deleted tuple whose xamx is in between cutoff_xid and OldestXmin. Since cutoff_xid depends on vacuum_freeze_min_age and autovacuum_freeze_max_age, it'll not be encountered easily. But, I think it can be reproduced with some xid burner patch. I think the fix should be something like following: if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask) && - TransactionIdDidCommit(xid)) + TransactionIdDidCommit(xid) && + !HeapTupleHeaderIsHotUpdated(tuple)) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg_internal("cannot freeze committed xmax %u", xid))); - freeze_xmax = true; + + freeze_xmax = HeapTupleHeaderIsHotUpdated(tuple) ? false : true; Attached a patch for the same. Thoughts? -- Thanks & Regards, Kuntal Ghosh
0001-Fix-sanity-check-for-HOT-updated-tuple-when-freezing.patch
Description: Binary data