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

Commit 3424bff

Browse files
committed
Prevent index-only scans from returning wrong answers under Hot Standby.
The alternative of disallowing index-only scans in HS operation was discussed, but the consensus was that it was better to treat marking a page all-visible as a recovery conflict for snapshots that could still fail to see XIDs on that page. We may in the future try to soften this, so that we simply force index scans to do heap fetches in cases where this may be an issue, rather than throwing a hard conflict.
1 parent 92df220 commit 3424bff

File tree

7 files changed

+36
-11
lines changed

7 files changed

+36
-11
lines changed

src/backend/access/heap/heapam.c

+16-2
Original file line numberDiff line numberDiff line change
@@ -4368,14 +4368,16 @@ log_heap_freeze(Relation reln, Buffer buffer,
43684368
* and dirtied.
43694369
*/
43704370
XLogRecPtr
4371-
log_heap_visible(RelFileNode rnode, BlockNumber block, Buffer vm_buffer)
4371+
log_heap_visible(RelFileNode rnode, BlockNumber block, Buffer vm_buffer,
4372+
TransactionId cutoff_xid)
43724373
{
43734374
xl_heap_visible xlrec;
43744375
XLogRecPtr recptr;
43754376
XLogRecData rdata[2];
43764377

43774378
xlrec.node = rnode;
43784379
xlrec.block = block;
4380+
xlrec.cutoff_xid = cutoff_xid;
43794381

43804382
rdata[0].data = (char *) &xlrec;
43814383
rdata[0].len = SizeOfHeapVisible;
@@ -4708,6 +4710,17 @@ heap_xlog_visible(XLogRecPtr lsn, XLogRecord *record)
47084710
return;
47094711
page = (Page) BufferGetPage(buffer);
47104712

4713+
/*
4714+
* If there are any Hot Standby transactions running that have an xmin
4715+
* horizon old enough that this page isn't all-visible for them, they
4716+
* might incorrectly decide that an index-only scan can skip a heap fetch.
4717+
*
4718+
* NB: It might be better to throw some kind of "soft" conflict here that
4719+
* forces any index-only scan that is in flight to perform heap fetches,
4720+
* rather than killing the transaction outright.
4721+
*/
4722+
ResolveRecoveryConflictWithSnapshot(xlrec->cutoff_xid, xlrec->node);
4723+
47114724
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
47124725

47134726
/*
@@ -4760,7 +4773,8 @@ heap_xlog_visible(XLogRecPtr lsn, XLogRecord *record)
47604773
* harm is done; and the next VACUUM will fix it.
47614774
*/
47624775
if (!XLByteLE(lsn, PageGetLSN(BufferGetPage(vmbuffer))))
4763-
visibilitymap_set(reln, xlrec->block, lsn, vmbuffer);
4776+
visibilitymap_set(reln, xlrec->block, lsn, vmbuffer,
4777+
xlrec->cutoff_xid);
47644778

47654779
ReleaseBuffer(vmbuffer);
47664780
FreeFakeRelcacheEntry(reln);

src/backend/access/heap/visibilitymap.c

+6-3
Original file line numberDiff line numberDiff line change
@@ -229,15 +229,17 @@ visibilitymap_pin_ok(BlockNumber heapBlk, Buffer buf)
229229
* recptr is the LSN of the XLOG record we're replaying, if we're in recovery,
230230
* or InvalidXLogRecPtr in normal running. The page LSN is advanced to the
231231
* one provided; in normal running, we generate a new XLOG record and set the
232-
* page LSN to that value.
232+
* page LSN to that value. cutoff_xid is the largest xmin on the page being
233+
* marked all-visible; it is needed for Hot Standby, and can be
234+
* InvalidTransactionId if the page contains no tuples.
233235
*
234236
* You must pass a buffer containing the correct map page to this function.
235237
* Call visibilitymap_pin first to pin the right one. This function doesn't do
236238
* any I/O.
237239
*/
238240
void
239241
visibilitymap_set(Relation rel, BlockNumber heapBlk, XLogRecPtr recptr,
240-
Buffer buf)
242+
Buffer buf, TransactionId cutoff_xid)
241243
{
242244
BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
243245
uint32 mapByte = HEAPBLK_TO_MAPBYTE(heapBlk);
@@ -269,7 +271,8 @@ visibilitymap_set(Relation rel, BlockNumber heapBlk, XLogRecPtr recptr,
269271
if (RelationNeedsWAL(rel))
270272
{
271273
if (XLogRecPtrIsInvalid(recptr))
272-
recptr = log_heap_visible(rel->rd_node, heapBlk, buf);
274+
recptr = log_heap_visible(rel->rd_node, heapBlk, buf,
275+
cutoff_xid);
273276
PageSetLSN(page, recptr);
274277
PageSetTLI(page, ThisTimeLineID);
275278
}

src/backend/commands/vacuumlazy.c

+9-2
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
448448
bool all_visible_according_to_vm;
449449
bool all_visible;
450450
bool has_dead_tuples;
451+
TransactionId visibility_cutoff_xid = InvalidTransactionId;
451452

452453
if (blkno == next_not_all_visible_block)
453454
{
@@ -627,7 +628,8 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
627628
{
628629
PageSetAllVisible(page);
629630
MarkBufferDirty(buf);
630-
visibilitymap_set(onerel, blkno, InvalidXLogRecPtr, vmbuffer);
631+
visibilitymap_set(onerel, blkno, InvalidXLogRecPtr, vmbuffer,
632+
InvalidTransactionId);
631633
}
632634

633635
UnlockReleaseBuffer(buf);
@@ -759,6 +761,10 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
759761
all_visible = false;
760762
break;
761763
}
764+
765+
/* Track newest xmin on page. */
766+
if (TransactionIdFollows(xmin, visibility_cutoff_xid))
767+
visibility_cutoff_xid = xmin;
762768
}
763769
break;
764770
case HEAPTUPLE_RECENTLY_DEAD:
@@ -853,7 +859,8 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
853859
PageSetAllVisible(page);
854860
MarkBufferDirty(buf);
855861
}
856-
visibilitymap_set(onerel, blkno, InvalidXLogRecPtr, vmbuffer);
862+
visibilitymap_set(onerel, blkno, InvalidXLogRecPtr, vmbuffer,
863+
visibility_cutoff_xid);
857864
}
858865

859866
/*

src/include/access/heapam.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ extern XLogRecPtr log_heap_freeze(Relation reln, Buffer buffer,
141141
TransactionId cutoff_xid,
142142
OffsetNumber *offsets, int offcnt);
143143
extern XLogRecPtr log_heap_visible(RelFileNode rnode, BlockNumber block,
144-
Buffer vm_buffer);
144+
Buffer vm_buffer, TransactionId cutoff_xid);
145145
extern XLogRecPtr log_newpage(RelFileNode *rnode, ForkNumber forkNum,
146146
BlockNumber blk, Page page);
147147

src/include/access/htup.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -788,9 +788,10 @@ typedef struct xl_heap_visible
788788
{
789789
RelFileNode node;
790790
BlockNumber block;
791+
TransactionId cutoff_xid;
791792
} xl_heap_visible;
792793

793-
#define SizeOfHeapVisible (offsetof(xl_heap_visible, block) + sizeof(BlockNumber))
794+
#define SizeOfHeapVisible (offsetof(xl_heap_visible, cutoff_xid) + sizeof(TransactionId))
794795

795796
extern void HeapTupleHeaderAdvanceLatestRemovedXid(HeapTupleHeader tuple,
796797
TransactionId *latestRemovedXid);

src/include/access/visibilitymap.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ extern void visibilitymap_pin(Relation rel, BlockNumber heapBlk,
2525
Buffer *vmbuf);
2626
extern bool visibilitymap_pin_ok(BlockNumber heapBlk, Buffer vmbuf);
2727
extern void visibilitymap_set(Relation rel, BlockNumber heapBlk,
28-
XLogRecPtr recptr, Buffer vmbuf);
28+
XLogRecPtr recptr, Buffer vmbuf, TransactionId cutoff_xid);
2929
extern bool visibilitymap_test(Relation rel, BlockNumber heapBlk, Buffer *vmbuf);
3030
extern BlockNumber visibilitymap_count(Relation rel);
3131
extern void visibilitymap_truncate(Relation rel, BlockNumber nheapblocks);

src/include/access/xlog_internal.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ typedef struct XLogContRecord
7171
/*
7272
* Each page of XLOG file has a header like this:
7373
*/
74-
#define XLOG_PAGE_MAGIC 0xD070 /* can be used as WAL version indicator */
74+
#define XLOG_PAGE_MAGIC 0xD071 /* can be used as WAL version indicator */
7575

7676
typedef struct XLogPageHeaderData
7777
{

0 commit comments

Comments
 (0)