On Thu, Nov 5, 2026 at 12:05 AM Zhijie Hou <[email protected]> wrote: > > Hi, > > > Once a tuple is frozen, its raw t_xmin is retained only for debugging > purposes; its commit timestamp entry may no longer be valid, or the > 32-bit XID may have wrapped around and been reassigned in a later epoch. > Using the raw xmin of a frozen tuple could therefore trigger spurious > update_origin_differs or delete_origin_differs conflicts, or report > bogus transaction ID, origin, and commit timestamp details for unique > constraint conflicts. >
I have a question about the basic premise here. As far as I can see, being frozen doesn't make the raw xmin wrong by itself. TransactionIdGetCommitTsData() already returns false when the xid falls outside [oldestCommitTsXid, newestCommitTsXid], so if commit_ts has been truncated past the xid, we just don't report a conflict, which is fine. Bogus data only comes back once the same 32-bit value has been reassigned and is back inside the commit_ts window, which needs on the order of 2^32 XIDs after the tuple was inserted. Until then, the raw xmin of a frozen tuple gives the correct origin and commit timestamp. With the patch, every frozen tuple is treated as having no xmin, so update_origin_differs / delete_origin_differs are never reported for it. When a tuple gets frozen depends on vacuum: vacuum_freeze_min_age, page-level opportunistic freezing since PG16, eager scanning of all-visible pages in PG18, or a manual VACUUM FREEZE. So for the same row and the same remote change, whether we report a genuine origin conflict would depend on whether autovacuum happened to freeze that page first, which makes it largely non-deterministic. That seems hard to explain to users, why the behaviour changes from case to case. The new test shows this. Rows (3,1) and (4,2) are inserted locally on node A and then frozen, and the subsequent UPDATE/DELETE come from B. The local origin (0) really does differ from B’s origin, so without VACUUM FREEZE these would be reported as update_origin_differs and delete_origin_differs, the mirror image of the delete_origin_differs case checked later in 035_conflicts.pl. The new test asserts that these real conflicts are no longer reported. So my question is whether being frozen is the right criterion, or whether we should try to address only the XID-reuse case. I don't immediately see a cheap way to tell a reused xid from the original with just 32 bits, so maybe the conservative behaviour is the price we have to pay. regards, Ajin Cherian Fujitsu Australia
