From 0b981977e53fd82088bfe41ff694ba2110ad523f Mon Sep 17 00:00:00 2001 From: bdrouvot Date: Sun, 13 Jun 2021 16:56:22 +0000 Subject: [PATCH v18 1/5] Add info in WAL records in preparation for logical slot conflict handling. When a WAL replay on standby indicates that a catalog table tuple is to be deleted by an xid that is greater than a logical slot's catalog_xmin, then that means the slot's catalog_xmin conflicts with the xid, and we need to handle the conflict. While subsequent commits will do the actual conflict handling, this commit adds a new field onCatalogTable in such WAL records, that is true for catalog tables, so as to arrange for conflict handling. Author: Andres Freund (in an older version), Amit Khandekar, Bertrand Drouvot Reviewed-By: Bertrand Drouvot, Andres Freund, Robert Haas, Fabrizio de Royes Mello --- src/backend/access/gist/gist.c | 2 +- src/backend/access/gist/gistbuild.c | 2 +- src/backend/access/gist/gistutil.c | 4 ++-- src/backend/access/gist/gistxlog.c | 4 +++- src/backend/access/hash/hashinsert.c | 1 + src/backend/access/heap/heapam.c | 4 +++- src/backend/access/heap/pruneheap.c | 1 + src/backend/access/heap/visibilitymap.c | 2 +- src/backend/access/nbtree/nbtpage.c | 12 +++++++++--- src/backend/access/spgist/spgvacuum.c | 8 ++++++++ src/backend/utils/cache/lsyscache.c | 15 +++++++++++++++ src/include/access/gist_private.h | 6 +++--- src/include/access/gistxlog.h | 3 ++- src/include/access/hash_xlog.h | 1 + src/include/access/heapam_xlog.h | 5 ++++- src/include/access/nbtxlog.h | 2 ++ src/include/access/spgxlog.h | 1 + src/include/utils/lsyscache.h | 1 + src/include/utils/rel.h | 9 +++++++++ 19 files changed, 68 insertions(+), 15 deletions(-) 17.9% src/backend/access/gist/ 13.1% src/backend/access/heap/ 14.5% src/backend/access/nbtree/ 8.9% src/backend/access/spgist/ 7.2% src/backend/utils/cache/ 19.6% src/include/access/ 16.6% src/include/utils/ diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c index 0683f42c25..b6e6340c3c 100644 --- a/src/backend/access/gist/gist.c +++ b/src/backend/access/gist/gist.c @@ -348,7 +348,7 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate, for (; ptr; ptr = ptr->next) { /* Allocate new page */ - ptr->buffer = gistNewBuffer(rel); + ptr->buffer = gistNewBuffer(heapRel, rel); GISTInitBuffer(ptr->buffer, (is_leaf) ? F_LEAF : 0); ptr->page = BufferGetPage(ptr->buffer); ptr->block.blkno = BufferGetBlockNumber(ptr->buffer); diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c index f46a42197c..80949bd4db 100644 --- a/src/backend/access/gist/gistbuild.c +++ b/src/backend/access/gist/gistbuild.c @@ -290,7 +290,7 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) Page page; /* initialize the root page */ - buffer = gistNewBuffer(index); + buffer = gistNewBuffer(heap, index); Assert(BufferGetBlockNumber(buffer) == GIST_ROOT_BLKNO); page = BufferGetPage(buffer); diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c index 8dcd53c457..8ba6178a5f 100644 --- a/src/backend/access/gist/gistutil.c +++ b/src/backend/access/gist/gistutil.c @@ -821,7 +821,7 @@ gistcheckpage(Relation rel, Buffer buf) * Caller is responsible for initializing the page by calling GISTInitBuffer */ Buffer -gistNewBuffer(Relation r) +gistNewBuffer(Relation heapRel, Relation r) { Buffer buffer; bool needLock; @@ -865,7 +865,7 @@ gistNewBuffer(Relation r) * page's deleteXid. */ if (XLogStandbyInfoActive() && RelationNeedsWAL(r)) - gistXLogPageReuse(r, blkno, GistPageGetDeleteXid(page)); + gistXLogPageReuse(heapRel, r, blkno, GistPageGetDeleteXid(page)); return buffer; } diff --git a/src/backend/access/gist/gistxlog.c b/src/backend/access/gist/gistxlog.c index 6464cb9281..46aee6f2a9 100644 --- a/src/backend/access/gist/gistxlog.c +++ b/src/backend/access/gist/gistxlog.c @@ -596,7 +596,8 @@ gistXLogAssignLSN(void) * Write XLOG record about reuse of a deleted page. */ void -gistXLogPageReuse(Relation rel, BlockNumber blkno, FullTransactionId latestRemovedXid) +gistXLogPageReuse(Relation heapRel, Relation rel, + BlockNumber blkno, FullTransactionId latestRemovedXid) { gistxlogPageReuse xlrec_reuse; @@ -607,6 +608,7 @@ gistXLogPageReuse(Relation rel, BlockNumber blkno, FullTransactionId latestRemov */ /* XLOG stuff */ + xlrec_reuse.onCatalogTable = RelationIsAccessibleInLogicalDecoding(heapRel); xlrec_reuse.node = rel->rd_node; xlrec_reuse.block = blkno; xlrec_reuse.latestRemovedFullXid = latestRemovedXid; diff --git a/src/backend/access/hash/hashinsert.c b/src/backend/access/hash/hashinsert.c index d254a00b6a..ce223b2c19 100644 --- a/src/backend/access/hash/hashinsert.c +++ b/src/backend/access/hash/hashinsert.c @@ -398,6 +398,7 @@ _hash_vacuum_one_page(Relation rel, Relation hrel, Buffer metabuf, Buffer buf) xl_hash_vacuum_one_page xlrec; XLogRecPtr recptr; + xlrec.onCatalogTable = RelationIsAccessibleInLogicalDecoding(hrel); xlrec.latestRemovedXid = latestRemovedXid; xlrec.ntuples = ndeletable; diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 2433998f39..4019a2122e 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -7941,6 +7941,7 @@ log_heap_freeze(Relation reln, Buffer buffer, TransactionId cutoff_xid, /* nor when there are no tuples to freeze */ Assert(ntuples > 0); + xlrec.onCatalogTable = RelationIsAccessibleInLogicalDecoding(reln); xlrec.cutoff_xid = cutoff_xid; xlrec.ntuples = ntuples; @@ -7971,7 +7972,7 @@ log_heap_freeze(Relation reln, Buffer buffer, TransactionId cutoff_xid, * heap_buffer, if necessary. */ XLogRecPtr -log_heap_visible(RelFileNode rnode, Buffer heap_buffer, Buffer vm_buffer, +log_heap_visible(Relation rel, Buffer heap_buffer, Buffer vm_buffer, TransactionId cutoff_xid, uint8 vmflags) { xl_heap_visible xlrec; @@ -7981,6 +7982,7 @@ log_heap_visible(RelFileNode rnode, Buffer heap_buffer, Buffer vm_buffer, Assert(BufferIsValid(heap_buffer)); Assert(BufferIsValid(vm_buffer)); + xlrec.onCatalogTable = RelationIsAccessibleInLogicalDecoding(rel); xlrec.cutoff_xid = cutoff_xid; xlrec.flags = vmflags; XLogBeginInsert(); diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c index 15ca1b304a..0590b7053c 100644 --- a/src/backend/access/heap/pruneheap.c +++ b/src/backend/access/heap/pruneheap.c @@ -323,6 +323,7 @@ heap_page_prune(Relation relation, Buffer buffer, xlrec.latestRemovedXid = prstate.latestRemovedXid; xlrec.nredirected = prstate.nredirected; xlrec.ndead = prstate.ndead; + xlrec.onCatalogTable = RelationIsAccessibleInLogicalDecoding(relation); XLogBeginInsert(); XLogRegisterData((char *) &xlrec, SizeOfHeapPrune); diff --git a/src/backend/access/heap/visibilitymap.c b/src/backend/access/heap/visibilitymap.c index e198df65d8..6e89a08c52 100644 --- a/src/backend/access/heap/visibilitymap.c +++ b/src/backend/access/heap/visibilitymap.c @@ -282,7 +282,7 @@ visibilitymap_set(Relation rel, BlockNumber heapBlk, Buffer heapBuf, if (XLogRecPtrIsInvalid(recptr)) { Assert(!InRecovery); - recptr = log_heap_visible(rel->rd_node, heapBuf, vmBuf, + recptr = log_heap_visible(rel, heapBuf, vmBuf, cutoff_xid, flags); /* diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c index ebec8fa5b8..2d27a3f974 100644 --- a/src/backend/access/nbtree/nbtpage.c +++ b/src/backend/access/nbtree/nbtpage.c @@ -36,6 +36,7 @@ #include "utils/memdebug.h" #include "utils/memutils.h" #include "utils/snapmgr.h" +#include "utils/lsyscache.h" static BTMetaPageData *_bt_getmeta(Relation rel, Buffer metabuf); static void _bt_log_reuse_page(Relation rel, BlockNumber blkno, @@ -43,7 +44,8 @@ static void _bt_log_reuse_page(Relation rel, BlockNumber blkno, static void _bt_delitems_delete(Relation rel, Buffer buf, TransactionId latestRemovedXid, OffsetNumber *deletable, int ndeletable, - BTVacuumPosting *updatable, int nupdatable); + BTVacuumPosting *updatable, int nupdatable, + Relation heapRel); static char *_bt_delitems_update(BTVacuumPosting *updatable, int nupdatable, OffsetNumber *updatedoffsets, Size *updatedbuflen, bool needswal); @@ -836,6 +838,7 @@ _bt_log_reuse_page(Relation rel, BlockNumber blkno, FullTransactionId safexid) */ /* XLOG stuff */ + xlrec_reuse.onCatalogTable = get_rel_logical_catalog(rel->rd_index->indrelid); xlrec_reuse.node = rel->rd_node; xlrec_reuse.block = blkno; xlrec_reuse.latestRemovedFullXid = safexid; @@ -1296,7 +1299,8 @@ _bt_delitems_vacuum(Relation rel, Buffer buf, static void _bt_delitems_delete(Relation rel, Buffer buf, TransactionId latestRemovedXid, OffsetNumber *deletable, int ndeletable, - BTVacuumPosting *updatable, int nupdatable) + BTVacuumPosting *updatable, int nupdatable, + Relation heapRel) { Page page = BufferGetPage(buf); BTPageOpaque opaque; @@ -1358,6 +1362,8 @@ _bt_delitems_delete(Relation rel, Buffer buf, TransactionId latestRemovedXid, XLogRecPtr recptr; xl_btree_delete xlrec_delete; + xlrec_delete.onCatalogTable = + RelationIsAccessibleInLogicalDecoding(heapRel); xlrec_delete.latestRemovedXid = latestRemovedXid; xlrec_delete.ndeleted = ndeletable; xlrec_delete.nupdated = nupdatable; @@ -1685,7 +1691,7 @@ _bt_delitems_delete_check(Relation rel, Buffer buf, Relation heapRel, /* Physically delete tuples (or TIDs) using deletable (or updatable) */ _bt_delitems_delete(rel, buf, latestRemovedXid, deletable, ndeletable, - updatable, nupdatable); + updatable, nupdatable, heapRel); /* be tidy */ for (int i = 0; i < nupdatable; i++) diff --git a/src/backend/access/spgist/spgvacuum.c b/src/backend/access/spgist/spgvacuum.c index 76fb0374c4..3186885d14 100644 --- a/src/backend/access/spgist/spgvacuum.c +++ b/src/backend/access/spgist/spgvacuum.c @@ -27,6 +27,7 @@ #include "storage/indexfsm.h" #include "storage/lmgr.h" #include "utils/snapmgr.h" +#include "utils/lsyscache.h" /* Entry in pending-list of TIDs we need to revisit */ @@ -503,6 +504,13 @@ vacuumRedirectAndPlaceholder(Relation index, Buffer buffer) spgxlogVacuumRedirect xlrec; GlobalVisState *vistest; + /* + * There is no chance of endless recursion even when we are doing catalog + * acceses here; because, spgist is never used for catalogs. Check + * comments in RelationIsAccessibleInLogicalDecoding(). + */ + xlrec.onCatalogTable = get_rel_logical_catalog(index->rd_index->indrelid); + xlrec.nToPlaceholder = 0; xlrec.newestRedirectXid = InvalidTransactionId; diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 6bba5f8ec4..28b1f961f4 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -18,6 +18,7 @@ #include "access/hash.h" #include "access/htup_details.h" #include "access/nbtree.h" +#include "access/table.h" #include "bootstrap/bootstrap.h" #include "catalog/namespace.h" #include "catalog/pg_am.h" @@ -2062,6 +2063,20 @@ get_rel_persistence(Oid relid) return result; } +bool +get_rel_logical_catalog(Oid relid) +{ + bool res; + Relation rel; + + /* assume previously locked */ + rel = table_open(relid, NoLock); + res = RelationIsAccessibleInLogicalDecoding(rel); + table_close(rel, NoLock); + + return res; +} + /* ---------- TRANSFORM CACHE ---------- */ diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h index 553d364e2d..a0f4015556 100644 --- a/src/include/access/gist_private.h +++ b/src/include/access/gist_private.h @@ -440,8 +440,8 @@ extern XLogRecPtr gistXLogPageDelete(Buffer buffer, FullTransactionId xid, Buffer parentBuffer, OffsetNumber downlinkOffset); -extern void gistXLogPageReuse(Relation rel, BlockNumber blkno, - FullTransactionId latestRemovedXid); +extern void gistXLogPageReuse(Relation heapRel, Relation rel, + BlockNumber blkno, FullTransactionId latestRemovedXid); extern XLogRecPtr gistXLogUpdate(Buffer buffer, OffsetNumber *todelete, int ntodelete, @@ -485,7 +485,7 @@ extern bool gistproperty(Oid index_oid, int attno, extern bool gistfitpage(IndexTuple *itvec, int len); extern bool gistnospace(Page page, IndexTuple *itvec, int len, OffsetNumber todelete, Size freespace); extern void gistcheckpage(Relation rel, Buffer buf); -extern Buffer gistNewBuffer(Relation r); +extern Buffer gistNewBuffer(Relation heapRel, Relation r); extern bool gistPageRecyclable(Page page); extern void gistfillbuffer(Page page, IndexTuple *itup, int len, OffsetNumber off); diff --git a/src/include/access/gistxlog.h b/src/include/access/gistxlog.h index fd5144f258..73999ddc70 100644 --- a/src/include/access/gistxlog.h +++ b/src/include/access/gistxlog.h @@ -49,9 +49,9 @@ typedef struct gistxlogPageUpdate */ typedef struct gistxlogDelete { + bool onCatalogTable; TransactionId latestRemovedXid; uint16 ntodelete; /* number of deleted offsets */ - /* * In payload of blk 0 : todelete OffsetNumbers */ @@ -97,6 +97,7 @@ typedef struct gistxlogPageDelete */ typedef struct gistxlogPageReuse { + bool onCatalogTable; RelFileNode node; BlockNumber block; FullTransactionId latestRemovedFullXid; diff --git a/src/include/access/hash_xlog.h b/src/include/access/hash_xlog.h index 4353a32dbb..94c3292c1e 100644 --- a/src/include/access/hash_xlog.h +++ b/src/include/access/hash_xlog.h @@ -250,6 +250,7 @@ typedef struct xl_hash_init_bitmap_page */ typedef struct xl_hash_vacuum_one_page { + bool onCatalogTable; TransactionId latestRemovedXid; int ntuples; diff --git a/src/include/access/heapam_xlog.h b/src/include/access/heapam_xlog.h index 27db48184e..eba48b0aee 100644 --- a/src/include/access/heapam_xlog.h +++ b/src/include/access/heapam_xlog.h @@ -242,6 +242,7 @@ typedef struct xl_heap_update */ typedef struct xl_heap_prune { + bool onCatalogTable; TransactionId latestRemovedXid; uint16 nredirected; uint16 ndead; @@ -338,6 +339,7 @@ typedef struct xl_heap_freeze_tuple */ typedef struct xl_heap_freeze_page { + bool onCatalogTable; TransactionId cutoff_xid; uint16 ntuples; } xl_heap_freeze_page; @@ -352,6 +354,7 @@ typedef struct xl_heap_freeze_page */ typedef struct xl_heap_visible { + bool onCatalogTable; TransactionId cutoff_xid; uint8 flags; } xl_heap_visible; @@ -413,7 +416,7 @@ extern bool heap_prepare_freeze_tuple(HeapTupleHeader tuple, bool *totally_frozen); extern void heap_execute_freeze_tuple(HeapTupleHeader tuple, xl_heap_freeze_tuple *xlrec_tp); -extern XLogRecPtr log_heap_visible(RelFileNode rnode, Buffer heap_buffer, +extern XLogRecPtr log_heap_visible(Relation rel, Buffer heap_buffer, Buffer vm_buffer, TransactionId cutoff_xid, uint8 flags); #endif /* HEAPAM_XLOG_H */ diff --git a/src/include/access/nbtxlog.h b/src/include/access/nbtxlog.h index 0f7731856b..b15aa47f1b 100644 --- a/src/include/access/nbtxlog.h +++ b/src/include/access/nbtxlog.h @@ -185,6 +185,7 @@ typedef struct xl_btree_dedup */ typedef struct xl_btree_reuse_page { + bool onCatalogTable; RelFileNode node; BlockNumber block; FullTransactionId latestRemovedFullXid; @@ -232,6 +233,7 @@ typedef struct xl_btree_vacuum typedef struct xl_btree_delete { + bool onCatalogTable; TransactionId latestRemovedXid; uint16 ndeleted; uint16 nupdated; diff --git a/src/include/access/spgxlog.h b/src/include/access/spgxlog.h index 69405b5750..06b91f4d04 100644 --- a/src/include/access/spgxlog.h +++ b/src/include/access/spgxlog.h @@ -237,6 +237,7 @@ typedef struct spgxlogVacuumRoot typedef struct spgxlogVacuumRedirect { + bool onCatalogTable; uint16 nToPlaceholder; /* number of redirects to make placeholders */ OffsetNumber firstPlaceholder; /* first placeholder tuple to remove */ TransactionId newestRedirectXid; /* newest XID of removed redirects */ diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index 77871aaefc..e2a5efed30 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -139,6 +139,7 @@ extern char get_rel_relkind(Oid relid); extern bool get_rel_relispartition(Oid relid); extern Oid get_rel_tablespace(Oid relid); extern char get_rel_persistence(Oid relid); +extern bool get_rel_logical_catalog(Oid relid); extern Oid get_transform_fromsql(Oid typid, Oid langid, List *trftypes); extern Oid get_transform_tosql(Oid typid, Oid langid, List *trftypes); extern bool get_typisdefined(Oid typid); diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 774ac5b2b1..7c016003a7 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -26,6 +26,7 @@ #include "storage/relfilenode.h" #include "utils/relcache.h" #include "utils/reltrigger.h" +#include "catalog/catalog.h" /* @@ -357,6 +358,9 @@ typedef struct StdRdOptions * RelationIsUsedAsCatalogTable * Returns whether the relation should be treated as a catalog table * from the pov of logical decoding. Note multiple eval of argument! + * This definition should not invoke anything that performs catalog + * access; otherwise it may cause infinite recursion. Check the comments + * in RelationIsAccessibleInLogicalDecoding() for details. */ #define RelationIsUsedAsCatalogTable(relation) \ ((relation)->rd_options && \ @@ -634,6 +638,11 @@ typedef struct ViewOptions * RelationIsAccessibleInLogicalDecoding * True if we need to log enough information to have access via * decoding snapshot. + * This definition should not invoke anything that performs catalog + * access. Otherwise, e.g. logging a WAL entry for catalog relation may + * invoke this function, which will in turn do catalog access, which may + * in turn cause another similar WAL entry to be logged, leading to + * infinite recursion. */ #define RelationIsAccessibleInLogicalDecoding(relation) \ (XLogLogicalInfoActive() && \ -- 2.18.4