From 9e792516465f204c98d566e21c8aee64b2849c73 Mon Sep 17 00:00:00 2001 From: Peter Geoghegan Date: Mon, 28 Dec 2020 20:57:43 -0800 Subject: [PATCH v1] Fix latestRemovedXid index deletion calculation. Author: Peter Geoghegan Discussion: https://postgr.es/m/CAH2-Wz=Eib393+HHcERK_9MtgNS7Ew1HY=RDC_g6GL46zM5C6Q@mail.gmail.com Backpatch: 12- --- src/backend/access/heap/heapam.c | 76 ++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 32 deletions(-) diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index a9583f3103..14621c99b3 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -6997,6 +6997,7 @@ heap_compute_xid_horizon_for_tuples(Relation rel, ItemPointerData *tids, int nitems) { + /* Start with the assumption that no recovery conflict is needed */ TransactionId latestRemovedXid = InvalidTransactionId; BlockNumber hblkno; Buffer buf = InvalidBuffer; @@ -7045,7 +7046,6 @@ heap_compute_xid_horizon_for_tuples(Relation rel, for (int i = 0; i < nitems; i++) { ItemPointer htid = &tids[i]; - ItemId hitemid; OffsetNumber hoffnum; /* @@ -7080,44 +7080,56 @@ heap_compute_xid_horizon_for_tuples(Relation rel, LockBuffer(buf, BUFFER_LOCK_SHARE); } + /* + * Maintain latestRemovedXid value for deletion operation as a whole + * by trying to advance current value using the tuple headers of items + * from the htid HOT chain + */ hoffnum = ItemPointerGetOffsetNumber(htid); - hitemid = PageGetItemId(hpage, hoffnum); - - /* - * Follow any redirections until we find something useful. - */ - while (ItemIdIsRedirected(hitemid)) + for (;;) { - hoffnum = ItemIdGetRedirect(hitemid); - hitemid = PageGetItemId(hpage, hoffnum); - } + ItemId hitemid = PageGetItemId(hpage, hoffnum); + HeapTupleHeader htup; - /* - * If the heap item has storage, then read the header and use that to - * set latestRemovedXid. - * - * Some LP_DEAD items may not be accessible, so we ignore them. - */ - if (ItemIdHasStorage(hitemid)) - { - HeapTupleHeader htuphdr; + if (ItemIdIsRedirected(hitemid)) + { + hoffnum = ItemIdGetRedirect(hitemid); + continue; + } - htuphdr = (HeapTupleHeader) PageGetItem(hpage, hitemid); - - HeapTupleHeaderAdvanceLatestRemovedXid(htuphdr, &latestRemovedXid); - } - else if (ItemIdIsDead(hitemid)) - { /* - * Conjecture: if hitemid is dead then it had xids before the xids - * marked on LP_NORMAL items. So we just ignore this item and move - * onto the next, for the purposes of calculating - * latestRemovedXid. + * It's quite possible that we'll encounter an LP_DEAD line + * pointer. No need to do anything more with htid when that + * happens. + * + * This is okay because the earlier heap pruning operation that + * made the line pointer LP_DEAD in the first place must have + * considered the heap tuple header as part of generating its own + * latestRemovedXid value. Relying on XLOG_HEAP2_CLEANUP_INFO + * records like this is the same strategy that conventional index + * vacuuming (not to be confused with index deletion) uses to + * totally avoid any need to explicitly log latestRemovedXid + * values. */ - } - else - Assert(!ItemIdIsUsed(hitemid)); + if (!ItemIdIsNormal(hitemid)) + break; + htup = (HeapTupleHeader) PageGetItem(hpage, hitemid); + HeapTupleHeaderAdvanceLatestRemovedXid(htup, &latestRemovedXid); + + /* + * If the tuple is not HOT-updated, then we are at the end of this + * HOT-chain. No need to visit later tuples from the same update + * chain (they get their own index entries) -- just move on to + * next htid from index AM caller. + */ + if (!HeapTupleHeaderIsHotUpdated(htup)) + break; + + /* Advance to next HOT chain member from htid's HOT chain */ + Assert(ItemPointerGetBlockNumber(&htup->t_ctid) == hblkno); + hoffnum = ItemPointerGetOffsetNumber(&htup->t_ctid); + } } if (BufferIsValid(buf)) -- 2.27.0