Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 962e0cc

Browse files
committed
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 d3abbbe. 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.
1 parent 7719ed0 commit 962e0cc

File tree

4 files changed

+27
-12
lines changed

4 files changed

+27
-12
lines changed

src/backend/access/spgist/spgutils.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,7 @@ spgFormDeadTuple(SpGistState *state, int tupstate,
722722
if (tupstate == SPGIST_REDIRECT)
723723
{
724724
ItemPointerSet(&tuple->pointer, blkno, offnum);
725+
Assert(TransactionIdIsValid(state->myXid));
725726
tuple->xid = state->myXid;
726727
}
727728
else

src/backend/access/spgist/spgvacuum.c

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
#include "storage/bufmgr.h"
2525
#include "storage/indexfsm.h"
2626
#include "storage/lmgr.h"
27-
#include "storage/procarray.h"
2827
#include "utils/snapmgr.h"
2928

3029

@@ -49,7 +48,6 @@ typedef struct spgBulkDeleteState
4948
SpGistState spgstate; /* for SPGiST operations that need one */
5049
spgVacPendingItem *pendingList; /* TIDs we need to (re)visit */
5150
TransactionId myXmin; /* for detecting newly-added redirects */
52-
TransactionId OldestXmin; /* for deciding a redirect is obsolete */
5351
BlockNumber lastFilledBlock; /* last non-deletable block */
5452
} spgBulkDeleteState;
5553

@@ -491,8 +489,7 @@ vacuumLeafRoot(spgBulkDeleteState *bds, Relation index, Buffer buffer)
491489
* Unlike the routines above, this works on both leaf and inner pages.
492490
*/
493491
static void
494-
vacuumRedirectAndPlaceholder(Relation index, Buffer buffer,
495-
TransactionId OldestXmin)
492+
vacuumRedirectAndPlaceholder(Relation index, Buffer buffer)
496493
{
497494
Page page = BufferGetPage(buffer);
498495
SpGistPageOpaque opaque = SpGistPageGetOpaque(page);
@@ -509,6 +506,7 @@ vacuumRedirectAndPlaceholder(Relation index, Buffer buffer,
509506
xlrec.node = index->rd_node;
510507
xlrec.blkno = BufferGetBlockNumber(buffer);
511508
xlrec.nToPlaceholder = 0;
509+
xlrec.newestRedirectXid = InvalidTransactionId;
512510

513511
START_CRIT_SECTION();
514512

@@ -526,13 +524,18 @@ vacuumRedirectAndPlaceholder(Relation index, Buffer buffer,
526524
dt = (SpGistDeadTuple) PageGetItem(page, PageGetItemId(page, i));
527525

528526
if (dt->tupstate == SPGIST_REDIRECT &&
529-
TransactionIdPrecedes(dt->xid, OldestXmin))
527+
TransactionIdPrecedes(dt->xid, RecentGlobalXmin))
530528
{
531529
dt->tupstate = SPGIST_PLACEHOLDER;
532530
Assert(opaque->nRedirection > 0);
533531
opaque->nRedirection--;
534532
opaque->nPlaceholder++;
535533

534+
/* remember newest XID among the removed redirects */
535+
if (!TransactionIdIsValid(xlrec.newestRedirectXid) ||
536+
TransactionIdPrecedes(xlrec.newestRedirectXid, dt->xid))
537+
xlrec.newestRedirectXid = dt->xid;
538+
536539
ItemPointerSetInvalid(&dt->pointer);
537540

538541
itemToPlaceholder[xlrec.nToPlaceholder] = i;
@@ -640,13 +643,13 @@ spgvacuumpage(spgBulkDeleteState *bds, BlockNumber blkno)
640643
else
641644
{
642645
vacuumLeafPage(bds, index, buffer, false);
643-
vacuumRedirectAndPlaceholder(index, buffer, bds->OldestXmin);
646+
vacuumRedirectAndPlaceholder(index, buffer);
644647
}
645648
}
646649
else
647650
{
648651
/* inner page */
649-
vacuumRedirectAndPlaceholder(index, buffer, bds->OldestXmin);
652+
vacuumRedirectAndPlaceholder(index, buffer);
650653
}
651654

652655
/*
@@ -723,7 +726,7 @@ spgprocesspending(spgBulkDeleteState *bds)
723726
/* deal with any deletable tuples */
724727
vacuumLeafPage(bds, index, buffer, true);
725728
/* might as well do this while we are here */
726-
vacuumRedirectAndPlaceholder(index, buffer, bds->OldestXmin);
729+
vacuumRedirectAndPlaceholder(index, buffer);
727730

728731
SpGistSetLastUsedPage(index, buffer);
729732

@@ -806,7 +809,6 @@ spgvacuumscan(spgBulkDeleteState *bds)
806809
initSpGistState(&bds->spgstate, index);
807810
bds->pendingList = NULL;
808811
bds->myXmin = GetActiveSnapshot()->xmin;
809-
bds->OldestXmin = GetOldestXmin(true, false);
810812
bds->lastFilledBlock = SPGIST_LAST_FIXED_BLKNO;
811813

812814
/*

src/backend/access/spgist/spgxlog.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@
1515
#include "postgres.h"
1616

1717
#include "access/spgist_private.h"
18+
#include "access/transam.h"
1819
#include "access/xlogutils.h"
19-
#include "storage/bufmgr.h"
20+
#include "storage/standby.h"
2021
#include "utils/memutils.h"
2122

2223

@@ -888,6 +889,15 @@ spgRedoVacuumRedirect(XLogRecPtr lsn, XLogRecord *record)
888889
ptr += sizeof(spgxlogVacuumRedirect);
889890
itemToPlaceholder = (OffsetNumber *) ptr;
890891

892+
/*
893+
* If any redirection tuples are being removed, make sure there are no
894+
* live Hot Standby transactions that might need to see them. This code
895+
* behaves similarly to btree's XLOG_BTREE_REUSE_PAGE case.
896+
*/
897+
if (InHotStandby && TransactionIdIsValid(xldata->newestRedirectXid))
898+
ResolveRecoveryConflictWithSnapshot(xldata->newestRedirectXid,
899+
xldata->node);
900+
891901
if (!(record->xl_info & XLR_BKP_BLOCK_1))
892902
{
893903
buffer = XLogReadBuffer(xldata->node, xldata->blkno, false);
@@ -1060,8 +1070,9 @@ spg_desc(StringInfo buf, uint8 xl_info, char *rec)
10601070
break;
10611071
case XLOG_SPGIST_VACUUM_REDIRECT:
10621072
out_target(buf, ((spgxlogVacuumRedirect *) rec)->node);
1063-
appendStringInfo(buf, "vacuum redirect tuples on page %u",
1064-
((spgxlogVacuumRedirect *) rec)->blkno);
1073+
appendStringInfo(buf, "vacuum redirect tuples on page %u, newest XID %u",
1074+
((spgxlogVacuumRedirect *) rec)->blkno,
1075+
((spgxlogVacuumRedirect *) rec)->newestRedirectXid);
10651076
break;
10661077
default:
10671078
appendStringInfo(buf, "unknown spgist op code %u", info);

src/include/access/spgist_private.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,7 @@ typedef struct spgxlogVacuumRedirect
591591
BlockNumber blkno; /* block number to clean */
592592
uint16 nToPlaceholder; /* number of redirects to make placeholders */
593593
OffsetNumber firstPlaceholder; /* first placeholder tuple to remove */
594+
TransactionId newestRedirectXid; /* newest XID of removed redirects */
594595

595596
/* offsets of redirect tuples to make placeholders follow */
596597
} spgxlogVacuumRedirect;

0 commit comments

Comments
 (0)