diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c index afa14841003505399155a06fd24796f382827796..72aae02b4506b7855848715caab2f66054757d07 100644 --- a/src/backend/access/spgist/spgutils.c +++ b/src/backend/access/spgist/spgutils.c @@ -722,6 +722,7 @@ spgFormDeadTuple(SpGistState *state, int tupstate, if (tupstate == SPGIST_REDIRECT) { ItemPointerSet(&tuple->pointer, blkno, offnum); + Assert(TransactionIdIsValid(state->myXid)); tuple->xid = state->myXid; } else diff --git a/src/backend/access/spgist/spgvacuum.c b/src/backend/access/spgist/spgvacuum.c index 27b55170cb4b3bfa083ce0d7ffa7cbbb1d1f2733..26bbd656c100ee96d530fdc3d0d93f28be094af4 100644 --- a/src/backend/access/spgist/spgvacuum.c +++ b/src/backend/access/spgist/spgvacuum.c @@ -24,7 +24,6 @@ #include "storage/bufmgr.h" #include "storage/indexfsm.h" #include "storage/lmgr.h" -#include "storage/procarray.h" #include "utils/snapmgr.h" @@ -49,7 +48,6 @@ typedef struct spgBulkDeleteState SpGistState spgstate; /* for SPGiST operations that need one */ spgVacPendingItem *pendingList; /* TIDs we need to (re)visit */ TransactionId myXmin; /* for detecting newly-added redirects */ - TransactionId OldestXmin; /* for deciding a redirect is obsolete */ BlockNumber lastFilledBlock; /* last non-deletable block */ } spgBulkDeleteState; @@ -491,8 +489,7 @@ vacuumLeafRoot(spgBulkDeleteState *bds, Relation index, Buffer buffer) * Unlike the routines above, this works on both leaf and inner pages. */ static void -vacuumRedirectAndPlaceholder(Relation index, Buffer buffer, - TransactionId OldestXmin) +vacuumRedirectAndPlaceholder(Relation index, Buffer buffer) { Page page = BufferGetPage(buffer); SpGistPageOpaque opaque = SpGistPageGetOpaque(page); @@ -509,6 +506,7 @@ vacuumRedirectAndPlaceholder(Relation index, Buffer buffer, xlrec.node = index->rd_node; xlrec.blkno = BufferGetBlockNumber(buffer); xlrec.nToPlaceholder = 0; + xlrec.newestRedirectXid = InvalidTransactionId; START_CRIT_SECTION(); @@ -526,13 +524,18 @@ vacuumRedirectAndPlaceholder(Relation index, Buffer buffer, dt = (SpGistDeadTuple) PageGetItem(page, PageGetItemId(page, i)); if (dt->tupstate == SPGIST_REDIRECT && - TransactionIdPrecedes(dt->xid, OldestXmin)) + TransactionIdPrecedes(dt->xid, RecentGlobalXmin)) { dt->tupstate = SPGIST_PLACEHOLDER; Assert(opaque->nRedirection > 0); opaque->nRedirection--; opaque->nPlaceholder++; + /* remember newest XID among the removed redirects */ + if (!TransactionIdIsValid(xlrec.newestRedirectXid) || + TransactionIdPrecedes(xlrec.newestRedirectXid, dt->xid)) + xlrec.newestRedirectXid = dt->xid; + ItemPointerSetInvalid(&dt->pointer); itemToPlaceholder[xlrec.nToPlaceholder] = i; @@ -640,13 +643,13 @@ spgvacuumpage(spgBulkDeleteState *bds, BlockNumber blkno) else { vacuumLeafPage(bds, index, buffer, false); - vacuumRedirectAndPlaceholder(index, buffer, bds->OldestXmin); + vacuumRedirectAndPlaceholder(index, buffer); } } else { /* inner page */ - vacuumRedirectAndPlaceholder(index, buffer, bds->OldestXmin); + vacuumRedirectAndPlaceholder(index, buffer); } /* @@ -723,7 +726,7 @@ spgprocesspending(spgBulkDeleteState *bds) /* deal with any deletable tuples */ vacuumLeafPage(bds, index, buffer, true); /* might as well do this while we are here */ - vacuumRedirectAndPlaceholder(index, buffer, bds->OldestXmin); + vacuumRedirectAndPlaceholder(index, buffer); SpGistSetLastUsedPage(index, buffer); @@ -806,7 +809,6 @@ spgvacuumscan(spgBulkDeleteState *bds) initSpGistState(&bds->spgstate, index); bds->pendingList = NULL; bds->myXmin = GetActiveSnapshot()->xmin; - bds->OldestXmin = GetOldestXmin(true, false); bds->lastFilledBlock = SPGIST_LAST_FIXED_BLKNO; /* diff --git a/src/backend/access/spgist/spgxlog.c b/src/backend/access/spgist/spgxlog.c index 82f8c8b978a01a426d4019aa5f7fdbfc463a5b36..45eda70ac765bdb39cba76d35127784c76821716 100644 --- a/src/backend/access/spgist/spgxlog.c +++ b/src/backend/access/spgist/spgxlog.c @@ -15,8 +15,9 @@ #include "postgres.h" #include "access/spgist_private.h" +#include "access/transam.h" #include "access/xlogutils.h" -#include "storage/bufmgr.h" +#include "storage/standby.h" #include "utils/memutils.h" @@ -888,6 +889,15 @@ spgRedoVacuumRedirect(XLogRecPtr lsn, XLogRecord *record) ptr += sizeof(spgxlogVacuumRedirect); itemToPlaceholder = (OffsetNumber *) ptr; + /* + * If any redirection tuples are being removed, make sure there are no + * live Hot Standby transactions that might need to see them. This code + * behaves similarly to btree's XLOG_BTREE_REUSE_PAGE case. + */ + if (InHotStandby && TransactionIdIsValid(xldata->newestRedirectXid)) + ResolveRecoveryConflictWithSnapshot(xldata->newestRedirectXid, + xldata->node); + if (!(record->xl_info & XLR_BKP_BLOCK_1)) { buffer = XLogReadBuffer(xldata->node, xldata->blkno, false); @@ -1060,8 +1070,9 @@ spg_desc(StringInfo buf, uint8 xl_info, char *rec) break; case XLOG_SPGIST_VACUUM_REDIRECT: out_target(buf, ((spgxlogVacuumRedirect *) rec)->node); - appendStringInfo(buf, "vacuum redirect tuples on page %u", - ((spgxlogVacuumRedirect *) rec)->blkno); + appendStringInfo(buf, "vacuum redirect tuples on page %u, newest XID %u", + ((spgxlogVacuumRedirect *) rec)->blkno, + ((spgxlogVacuumRedirect *) rec)->newestRedirectXid); break; default: appendStringInfo(buf, "unknown spgist op code %u", info); diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h index 74267a439002af2bc58806b0898a02098c2b5465..b5bc45121aa8a84cf30609aa9302897b447f5625 100644 --- a/src/include/access/spgist_private.h +++ b/src/include/access/spgist_private.h @@ -591,6 +591,7 @@ typedef struct spgxlogVacuumRedirect BlockNumber blkno; /* block number to clean */ uint16 nToPlaceholder; /* number of redirects to make placeholders */ OffsetNumber firstPlaceholder; /* first placeholder tuple to remove */ + TransactionId newestRedirectXid; /* newest XID of removed redirects */ /* offsets of redirect tuples to make placeholders follow */ } spgxlogVacuumRedirect;