diff options
author | Heikki Linnakangas <heikki.linnakangas@iki.fi> | 2019-07-24 20:24:07 +0300 |
---|---|---|
committer | Heikki Linnakangas <heikki.linnakangas@iki.fi> | 2019-07-24 20:25:22 +0300 |
commit | fb5344c969af77bb78bc2a643fb75b9f8fea72dd (patch) | |
tree | bd4e4af8e04137ff40c32024ea55b53031852b62 /src/include | |
parent | e2e992c93145cfc0e3563fb84efd25b390a84bb9 (diff) | |
download | postgresql-fb5344c969af77bb78bc2a643fb75b9f8fea72dd.tar.gz postgresql-fb5344c969af77bb78bc2a643fb75b9f8fea72dd.zip |
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 unrecyclable
again. 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 Borodin
Discussion: https://www.postgresql.org/message-id/835A15A5-F1B4-4446-A711-BF48357EB602%40yandex-team.ru
Diffstat (limited to 'src/include')
-rw-r--r-- | src/include/access/gist.h | 45 | ||||
-rw-r--r-- | src/include/access/gist_private.h | 4 | ||||
-rw-r--r-- | src/include/access/gistxlog.h | 6 | ||||
-rw-r--r-- | src/include/utils/snapmgr.h | 3 |
4 files changed, 48 insertions, 10 deletions
diff --git a/src/include/access/gist.h b/src/include/access/gist.h index 6902f4115b7..8292956cc09 100644 --- a/src/include/access/gist.h +++ b/src/include/access/gist.h @@ -16,6 +16,7 @@ #ifndef GIST_H #define GIST_H +#include "access/transam.h" #include "access/xlog.h" #include "access/xlogdefs.h" #include "storage/block.h" @@ -140,8 +141,6 @@ typedef struct GISTENTRY #define GIST_LEAF(entry) (GistPageIsLeaf((entry)->page)) #define GistPageIsDeleted(page) ( GistPageGetOpaque(page)->flags & F_DELETED) -#define GistPageSetDeleted(page) ( GistPageGetOpaque(page)->flags |= F_DELETED) -#define GistPageSetNonDeleted(page) ( GistPageGetOpaque(page)->flags &= ~F_DELETED) #define GistTuplesDeleted(page) ( GistPageGetOpaque(page)->flags & F_TUPLES_DELETED) #define GistMarkTuplesDeleted(page) ( GistPageGetOpaque(page)->flags |= F_TUPLES_DELETED) @@ -158,9 +157,45 @@ typedef struct GISTENTRY #define GistPageGetNSN(page) ( PageXLogRecPtrGet(GistPageGetOpaque(page)->nsn)) #define GistPageSetNSN(page, val) ( PageXLogRecPtrSet(GistPageGetOpaque(page)->nsn, val)) -/* For deleted pages we store last xid which could see the page in scan */ -#define GistPageGetDeleteXid(page) ( ((PageHeader) (page))->pd_prune_xid ) -#define GistPageSetDeleteXid(page, val) ( ((PageHeader) (page))->pd_prune_xid = val) + +/* + * On a deleted page, we store this struct. A deleted page doesn't contain any + * tuples, so we don't use the normal page layout with line pointers. Instead, + * this struct is stored right after the standard page header. pd_lower points + * to the end of this struct. If we add fields to this struct in the future, we + * can distinguish the old and new formats by pd_lower. + */ +typedef struct GISTDeletedPageContents +{ + /* last xid which could see the page in a scan */ + FullTransactionId deleteXid; +} GISTDeletedPageContents; + +static inline void +GistPageSetDeleted(Page page, FullTransactionId deletexid) +{ + Assert(PageIsEmpty(page)); + + GistPageGetOpaque(page)->flags |= F_DELETED; + ((PageHeader) page)->pd_lower = MAXALIGN(SizeOfPageHeaderData) + sizeof(GISTDeletedPageContents); + + ((GISTDeletedPageContents *) PageGetContents(page))->deleteXid = deletexid; +} + +static inline FullTransactionId +GistPageGetDeleteXid(Page page) +{ + Assert(GistPageIsDeleted(page)); + + /* Is the deleteXid field present? */ + if (((PageHeader) page)->pd_lower >= MAXALIGN(SizeOfPageHeaderData) + + offsetof(GISTDeletedPageContents, deleteXid) + sizeof(FullTransactionId)) + { + return ((GISTDeletedPageContents *) PageGetContents(page))->deleteXid; + } + else + return FullTransactionIdFromEpochAndXid(0, FirstNormalTransactionId); +} /* * Vector of GISTENTRY structs; user-defined methods union and picksplit diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h index f80694bf9a8..f84ea71ecba 100644 --- a/src/include/access/gist_private.h +++ b/src/include/access/gist_private.h @@ -426,11 +426,11 @@ extern SplitedPageLayout *gistSplit(Relation r, Page page, IndexTuple *itup, /* gistxlog.c */ extern XLogRecPtr gistXLogPageDelete(Buffer buffer, - TransactionId xid, Buffer parentBuffer, + FullTransactionId xid, Buffer parentBuffer, OffsetNumber downlinkOffset); extern void gistXLogPageReuse(Relation rel, BlockNumber blkno, - TransactionId latestRemovedXid); + FullTransactionId latestRemovedXid); extern XLogRecPtr gistXLogUpdate(Buffer buffer, OffsetNumber *todelete, int ntodelete, diff --git a/src/include/access/gistxlog.h b/src/include/access/gistxlog.h index 969a5376b5e..e44922d915c 100644 --- a/src/include/access/gistxlog.h +++ b/src/include/access/gistxlog.h @@ -83,7 +83,7 @@ typedef struct gistxlogPageSplit */ typedef struct gistxlogPageDelete { - TransactionId deleteXid; /* last Xid which could see page in scan */ + FullTransactionId deleteXid; /* last Xid which could see page in scan */ OffsetNumber downlinkOffset; /* Offset of downlink referencing this * page */ } gistxlogPageDelete; @@ -98,10 +98,10 @@ typedef struct gistxlogPageReuse { RelFileNode node; BlockNumber block; - TransactionId latestRemovedXid; + FullTransactionId latestRemovedFullXid; } gistxlogPageReuse; -#define SizeOfGistxlogPageReuse (offsetof(gistxlogPageReuse, latestRemovedXid) + sizeof(TransactionId)) +#define SizeOfGistxlogPageReuse (offsetof(gistxlogPageReuse, latestRemovedFullXid) + sizeof(FullTransactionId)) extern void gist_redo(XLogReaderState *record); extern void gist_desc(StringInfo buf, XLogReaderState *record); diff --git a/src/include/utils/snapmgr.h b/src/include/utils/snapmgr.h index 58ae3b0c7a1..6641ee510a1 100644 --- a/src/include/utils/snapmgr.h +++ b/src/include/utils/snapmgr.h @@ -13,6 +13,7 @@ #ifndef SNAPMGR_H #define SNAPMGR_H +#include "access/transam.h" #include "fmgr.h" #include "utils/relcache.h" #include "utils/resowner.h" @@ -122,6 +123,8 @@ extern void UnregisterSnapshot(Snapshot snapshot); extern Snapshot RegisterSnapshotOnOwner(Snapshot snapshot, ResourceOwner owner); extern void UnregisterSnapshotFromOwner(Snapshot snapshot, ResourceOwner owner); +extern FullTransactionId GetFullRecentGlobalXmin(void); + extern void AtSubCommit_Snapshot(int level); extern void AtSubAbort_Snapshot(int level); extern void AtEOXact_Snapshot(bool isCommit, bool resetXmin); |