Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitfb5344c

Browse files
committed
Use full 64-bit XID for checking if a deleted GiST page is old enough.
Otherwise, after a deleted page gets even older, it becomes unrecyclableagain. B-tree has the same problem, and has had since time immemorial,but let's at least fix this in GiST, where this is new.Backpatch to v12, where GiST page deletion was introduced.Reviewed-by: Andrey BorodinDiscussion:https://www.postgresql.org/message-id/835A15A5-F1B4-4446-A711-BF48357EB602%40yandex-team.ru
1 parente2e992c commitfb5344c

File tree

9 files changed

+134
-28
lines changed

9 files changed

+134
-28
lines changed

‎src/backend/access/gist/gistutil.c

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -882,9 +882,27 @@ gistNewBuffer(Relation r)
882882
bool
883883
gistPageRecyclable(Pagepage)
884884
{
885-
returnPageIsNew(page)||
886-
(GistPageIsDeleted(page)&&
887-
TransactionIdPrecedes(GistPageGetDeleteXid(page),RecentGlobalXmin));
885+
if (PageIsNew(page))
886+
return true;
887+
if (GistPageIsDeleted(page))
888+
{
889+
/*
890+
* The page was deleted, but when? If it was just deleted, a scan
891+
* might have seen the downlink to it, and will read the page later.
892+
* As long as that can happen, we must keep the deleted page around as
893+
* a tombstone.
894+
*
895+
* Compare the deletion XID with RecentGlobalXmin. If deleteXid <
896+
* RecentGlobalXmin, then no scan that's still in progress could have
897+
* seen its downlink, and we can recycle it.
898+
*/
899+
FullTransactionIddeletexid_full=GistPageGetDeleteXid(page);
900+
FullTransactionIdrecentxmin_full=GetFullRecentGlobalXmin();
901+
902+
if (FullTransactionIdPrecedes(deletexid_full,recentxmin_full))
903+
return true;
904+
}
905+
return false;
888906
}
889907

890908
bytea*

‎src/backend/access/gist/gistvacuum.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,7 @@ gistdeletepage(IndexVacuumInfo *info, GistBulkDeleteResult *stats,
595595
ItemIdiid;
596596
IndexTupleidxtuple;
597597
XLogRecPtrrecptr;
598-
TransactionIdtxid;
598+
FullTransactionIdtxid;
599599

600600
/*
601601
* Check that the leaf is still empty and deletable.
@@ -648,14 +648,13 @@ gistdeletepage(IndexVacuumInfo *info, GistBulkDeleteResult *stats,
648648
* currently in progress must have ended. (That's much more conservative
649649
* than needed, but let's keep it safe and simple.)
650650
*/
651-
txid=ReadNewTransactionId();
651+
txid=ReadNextFullTransactionId();
652652

653653
START_CRIT_SECTION();
654654

655655
/* mark the page as deleted */
656656
MarkBufferDirty(leafBuffer);
657-
GistPageSetDeleteXid(leafPage,txid);
658-
GistPageSetDeleted(leafPage);
657+
GistPageSetDeleted(leafPage,txid);
659658
stats->stats.pages_deleted++;
660659

661660
/* remove the downlink from the parent */

‎src/backend/access/gist/gistxlog.c

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -356,8 +356,7 @@ gistRedoPageDelete(XLogReaderState *record)
356356
{
357357
Pagepage= (Page)BufferGetPage(leafBuffer);
358358

359-
GistPageSetDeleteXid(page,xldata->deleteXid);
360-
GistPageSetDeleted(page);
359+
GistPageSetDeleted(page,xldata->deleteXid);
361360

362361
PageSetLSN(page,lsn);
363362
MarkBufferDirty(leafBuffer);
@@ -396,8 +395,27 @@ gistRedoPageReuse(XLogReaderState *record)
396395
*/
397396
if (InHotStandby)
398397
{
399-
ResolveRecoveryConflictWithSnapshot(xlrec->latestRemovedXid,
400-
xlrec->node);
398+
FullTransactionIdlatestRemovedFullXid=xlrec->latestRemovedFullXid;
399+
FullTransactionIdnextFullXid=ReadNextFullTransactionId();
400+
uint64diff;
401+
402+
/*
403+
* ResolveRecoveryConflictWithSnapshot operates on 32-bit
404+
* TransactionIds, so truncate the logged FullTransactionId. If the
405+
* logged value is very old, so that XID wrap-around already happened
406+
* on it, there can't be any snapshots that still see it.
407+
*/
408+
nextFullXid=ReadNextFullTransactionId();
409+
diff=U64FromFullTransactionId(nextFullXid)-
410+
U64FromFullTransactionId(latestRemovedFullXid);
411+
if (diff<MaxTransactionId /2)
412+
{
413+
TransactionIdlatestRemovedXid;
414+
415+
latestRemovedXid=XidFromFullTransactionId(latestRemovedFullXid);
416+
ResolveRecoveryConflictWithSnapshot(latestRemovedXid,
417+
xlrec->node);
418+
}
401419
}
402420
}
403421

@@ -554,7 +572,7 @@ gistXLogSplit(bool page_is_leaf,
554572
* downlink from the parent page.
555573
*/
556574
XLogRecPtr
557-
gistXLogPageDelete(Bufferbuffer,TransactionIdxid,
575+
gistXLogPageDelete(Bufferbuffer,FullTransactionIdxid,
558576
BufferparentBuffer,OffsetNumberdownlinkOffset)
559577
{
560578
gistxlogPageDeletexlrec;
@@ -578,7 +596,7 @@ gistXLogPageDelete(Buffer buffer, TransactionId xid,
578596
* Write XLOG record about reuse of a deleted page.
579597
*/
580598
void
581-
gistXLogPageReuse(Relationrel,BlockNumberblkno,TransactionIdlatestRemovedXid)
599+
gistXLogPageReuse(Relationrel,BlockNumberblkno,FullTransactionIdlatestRemovedXid)
582600
{
583601
gistxlogPageReusexlrec_reuse;
584602

@@ -591,7 +609,7 @@ gistXLogPageReuse(Relation rel, BlockNumber blkno, TransactionId latestRemovedXi
591609
/* XLOG stuff */
592610
xlrec_reuse.node=rel->rd_node;
593611
xlrec_reuse.block=blkno;
594-
xlrec_reuse.latestRemovedXid=latestRemovedXid;
612+
xlrec_reuse.latestRemovedFullXid=latestRemovedXid;
595613

596614
XLogBeginInsert();
597615
XLogRegisterData((char*)&xlrec_reuse,SizeOfGistxlogPageReuse);

‎src/backend/access/rmgrdesc/gistdesc.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@ out_gistxlogPageUpdate(StringInfo buf, gistxlogPageUpdate *xlrec)
2626
staticvoid
2727
out_gistxlogPageReuse(StringInfobuf,gistxlogPageReuse*xlrec)
2828
{
29-
appendStringInfo(buf,"rel %u/%u/%u; blk %u; latestRemovedXid %u",
29+
appendStringInfo(buf,"rel %u/%u/%u; blk %u; latestRemovedXid %u:%u",
3030
xlrec->node.spcNode,xlrec->node.dbNode,
3131
xlrec->node.relNode,xlrec->block,
32-
xlrec->latestRemovedXid);
32+
EpochFromFullTransactionId(xlrec->latestRemovedFullXid),
33+
XidFromFullTransactionId(xlrec->latestRemovedFullXid));
3334
}
3435

3536
staticvoid
@@ -50,8 +51,10 @@ out_gistxlogPageSplit(StringInfo buf, gistxlogPageSplit *xlrec)
5051
staticvoid
5152
out_gistxlogPageDelete(StringInfobuf,gistxlogPageDelete*xlrec)
5253
{
53-
appendStringInfo(buf,"deleteXid %u; downlink %u",
54-
xlrec->deleteXid,xlrec->downlinkOffset);
54+
appendStringInfo(buf,"deleteXid %u:%u; downlink %u",
55+
EpochFromFullTransactionId(xlrec->deleteXid),
56+
XidFromFullTransactionId(xlrec->deleteXid),
57+
xlrec->downlinkOffset);
5558
}
5659

5760
void

‎src/backend/utils/time/snapmgr.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,36 @@ xmin_cmp(const pairingheap_node *a, const pairingheap_node *b, void *arg)
956956
return0;
957957
}
958958

959+
/*
960+
* Get current RecentGlobalXmin value, as a FullTransactionId.
961+
*/
962+
FullTransactionId
963+
GetFullRecentGlobalXmin(void)
964+
{
965+
FullTransactionIdnextxid_full;
966+
uint32nextxid_epoch;
967+
TransactionIdnextxid_xid;
968+
uint32epoch;
969+
970+
Assert(TransactionIdIsNormal(RecentGlobalXmin));
971+
972+
/*
973+
* Compute the epoch from the next XID's epoch. This relies on the fact
974+
* that RecentGlobalXmin must be within the 2 billion XID horizon from the
975+
* next XID.
976+
*/
977+
nextxid_full=ReadNextFullTransactionId();
978+
nextxid_epoch=EpochFromFullTransactionId(nextxid_full);
979+
nextxid_xid=XidFromFullTransactionId(nextxid_full);
980+
981+
if (RecentGlobalXmin>nextxid_xid)
982+
epoch=nextxid_epoch-1;
983+
else
984+
epoch=nextxid_epoch;
985+
986+
returnFullTransactionIdFromEpochAndXid(epoch,RecentGlobalXmin);
987+
}
988+
959989
/*
960990
* SnapshotResetXmin
961991
*

‎src/include/access/gist.h

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#ifndefGIST_H
1717
#defineGIST_H
1818

19+
#include"access/transam.h"
1920
#include"access/xlog.h"
2021
#include"access/xlogdefs.h"
2122
#include"storage/block.h"
@@ -140,8 +141,6 @@ typedef struct GISTENTRY
140141
#defineGIST_LEAF(entry) (GistPageIsLeaf((entry)->page))
141142

142143
#defineGistPageIsDeleted(page) ( GistPageGetOpaque(page)->flags & F_DELETED)
143-
#defineGistPageSetDeleted(page)( GistPageGetOpaque(page)->flags |= F_DELETED)
144-
#defineGistPageSetNonDeleted(page) ( GistPageGetOpaque(page)->flags &= ~F_DELETED)
145144

146145
#defineGistTuplesDeleted(page) ( GistPageGetOpaque(page)->flags & F_TUPLES_DELETED)
147146
#defineGistMarkTuplesDeleted(page) ( GistPageGetOpaque(page)->flags |= F_TUPLES_DELETED)
@@ -158,9 +157,45 @@ typedef struct GISTENTRY
158157
#defineGistPageGetNSN(page) ( PageXLogRecPtrGet(GistPageGetOpaque(page)->nsn))
159158
#defineGistPageSetNSN(page,val) ( PageXLogRecPtrSet(GistPageGetOpaque(page)->nsn, val))
160159

161-
/* For deleted pages we store last xid which could see the page in scan */
162-
#defineGistPageGetDeleteXid(page) ( ((PageHeader) (page))->pd_prune_xid )
163-
#defineGistPageSetDeleteXid(page,val) ( ((PageHeader) (page))->pd_prune_xid = val)
160+
161+
/*
162+
* On a deleted page, we store this struct. A deleted page doesn't contain any
163+
* tuples, so we don't use the normal page layout with line pointers. Instead,
164+
* this struct is stored right after the standard page header. pd_lower points
165+
* to the end of this struct. If we add fields to this struct in the future, we
166+
* can distinguish the old and new formats by pd_lower.
167+
*/
168+
typedefstructGISTDeletedPageContents
169+
{
170+
/* last xid which could see the page in a scan */
171+
FullTransactionIddeleteXid;
172+
}GISTDeletedPageContents;
173+
174+
staticinlinevoid
175+
GistPageSetDeleted(Pagepage,FullTransactionIddeletexid)
176+
{
177+
Assert(PageIsEmpty(page));
178+
179+
GistPageGetOpaque(page)->flags |=F_DELETED;
180+
((PageHeader)page)->pd_lower=MAXALIGN(SizeOfPageHeaderData)+sizeof(GISTDeletedPageContents);
181+
182+
((GISTDeletedPageContents*)PageGetContents(page))->deleteXid=deletexid;
183+
}
184+
185+
staticinlineFullTransactionId
186+
GistPageGetDeleteXid(Pagepage)
187+
{
188+
Assert(GistPageIsDeleted(page));
189+
190+
/* Is the deleteXid field present? */
191+
if (((PageHeader)page)->pd_lower >=MAXALIGN(SizeOfPageHeaderData)+
192+
offsetof(GISTDeletedPageContents,deleteXid)+sizeof(FullTransactionId))
193+
{
194+
return ((GISTDeletedPageContents*)PageGetContents(page))->deleteXid;
195+
}
196+
else
197+
returnFullTransactionIdFromEpochAndXid(0,FirstNormalTransactionId);
198+
}
164199

165200
/*
166201
* Vector of GISTENTRY structs; user-defined methods union and picksplit

‎src/include/access/gist_private.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -426,11 +426,11 @@ extern SplitedPageLayout *gistSplit(Relation r, Page page, IndexTuple *itup,
426426

427427
/* gistxlog.c */
428428
externXLogRecPtrgistXLogPageDelete(Bufferbuffer,
429-
TransactionIdxid,BufferparentBuffer,
429+
FullTransactionIdxid,BufferparentBuffer,
430430
OffsetNumberdownlinkOffset);
431431

432432
externvoidgistXLogPageReuse(Relationrel,BlockNumberblkno,
433-
TransactionIdlatestRemovedXid);
433+
FullTransactionIdlatestRemovedXid);
434434

435435
externXLogRecPtrgistXLogUpdate(Bufferbuffer,
436436
OffsetNumber*todelete,intntodelete,

‎src/include/access/gistxlog.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ typedef struct gistxlogPageSplit
8383
*/
8484
typedefstructgistxlogPageDelete
8585
{
86-
TransactionIddeleteXid;/* last Xid which could see page in scan */
86+
FullTransactionIddeleteXid;/* last Xid which could see page in scan */
8787
OffsetNumberdownlinkOffset;/* Offset of downlink referencing this
8888
* page */
8989
}gistxlogPageDelete;
@@ -98,10 +98,10 @@ typedef struct gistxlogPageReuse
9898
{
9999
RelFileNodenode;
100100
BlockNumberblock;
101-
TransactionIdlatestRemovedXid;
101+
FullTransactionIdlatestRemovedFullXid;
102102
}gistxlogPageReuse;
103103

104-
#defineSizeOfGistxlogPageReuse(offsetof(gistxlogPageReuse,latestRemovedXid) + sizeof(TransactionId))
104+
#defineSizeOfGistxlogPageReuse(offsetof(gistxlogPageReuse,latestRemovedFullXid) + sizeof(FullTransactionId))
105105

106106
externvoidgist_redo(XLogReaderState*record);
107107
externvoidgist_desc(StringInfobuf,XLogReaderState*record);

‎src/include/utils/snapmgr.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#ifndefSNAPMGR_H
1414
#defineSNAPMGR_H
1515

16+
#include"access/transam.h"
1617
#include"fmgr.h"
1718
#include"utils/relcache.h"
1819
#include"utils/resowner.h"
@@ -122,6 +123,8 @@ extern void UnregisterSnapshot(Snapshot snapshot);
122123
externSnapshotRegisterSnapshotOnOwner(Snapshotsnapshot,ResourceOwnerowner);
123124
externvoidUnregisterSnapshotFromOwner(Snapshotsnapshot,ResourceOwnerowner);
124125

126+
externFullTransactionIdGetFullRecentGlobalXmin(void);
127+
125128
externvoidAtSubCommit_Snapshot(intlevel);
126129
externvoidAtSubAbort_Snapshot(intlevel);
127130
externvoidAtEOXact_Snapshot(boolisCommit,boolresetXmin);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp