mirror of
https://github.com/postgres/postgres.git
synced 2025-11-12 05:01:15 +03:00
Fix race conditions associated with SPGiST redirection tuples.
The correct test for whether a redirection tuple is removable is whether
tuple's xid < RecentGlobalXmin, not OldestXmin; the previous coding
failed to protect index searches being done in concurrent transactions that
have no XID. This mirrors the recent fix in btree's page recycling logic
made in commit d3abbbebe5.
Also, WAL-log the newest XID of any removed redirection tuple on an index
page, and apply ResolveRecoveryConflictWithSnapshot during InHotStandby WAL
replay. This protects against concurrent Hot Standby transactions possibly
needing to see the redirection tuple(s).
Per my query of 2012-03-12 and subsequent discussion.
This commit is contained in:
@@ -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;
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user