From f6f800819c497817c3f2957683fc521abe82c2b7 Mon Sep 17 00:00:00 2001 From: Greg Stark Date: Thu, 31 Mar 2022 15:49:19 -0400 Subject: [PATCH v8 2/3] Update relfrozenxmin when truncating temp tables Make ON COMMIT DELETE ROWS reset relfrozenxmin and other table stats to the same values used in initial table creation. Otherwise even typical short-lived transactions in long-lived sessions using temporary tables can easily cause them to reach transaction wraparound and autovacuum cannot come to the rescue for temporary tables. Also optimize the relminmxid used for for temporary tables to be our own oldest MultiXactId instead of the globally oldest one. This avoids the expensive calculation of the latter on every transaction commit. This code path is also used by truncation of tables created within the same transaction. --- src/backend/access/heap/heapam_handler.c | 51 +++++++++++++++++++++++- src/backend/access/transam/multixact.c | 15 +++++++ src/backend/catalog/heap.c | 14 ++++++- src/include/access/multixact.h | 1 + 4 files changed, 78 insertions(+), 3 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index ab1bcf3522..b01a25884f 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -33,6 +33,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/vacuum.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -587,9 +588,18 @@ heapam_relation_set_new_filelocator(Relation rel, * could reuse values from their local cache, so we are careful to * consider all currently running multis. * - * XXX this could be refined further, but is it worth the hassle? + * In the case of temporary tables we can refine this slightly and use a + * our own oldest visible MultiXactId. This is also cheaper to calculate + * which is nice since temporary tables might be getting created often. */ - *minmulti = GetOldestMultiXactId(); + if (persistence == RELPERSISTENCE_TEMP) + { + *minmulti = GetOurOldestMultiXactId(); + } + else + { + *minmulti = GetOldestMultiXactId(); + } srel = RelationCreateStorage(*newrlocator, persistence, true); @@ -618,6 +628,43 @@ heapam_relation_set_new_filelocator(Relation rel, static void heapam_relation_nontransactional_truncate(Relation rel) { + /* This function from vacuum.c does a non-transactional update of the + * catalog entry for this relation. That's ok because these values are + * always safe regardless of whether we commit and in any case this is + * either a temporary table or a filenode created in this transaction so + * this tuple will be irrelevant if we do not commit. It's also important + * to avoid lots of catalog bloat due to temporary tables being truncated + * on every transaction commit. + * + * We set in_outer_transaction=false because that controls whether + * vac_update_relstats updates other columns like relhasindex, + * relhasrules, relhastriggers which is not changing here. This is a bit + * of a hack, perhaps this parameter should change name. + * + * These parameters should stay in sync with + * heapam_relation_set_new_filelocator() above and AddNewRelationTuple() + * in heap.c. In theory this should probably return the relfrozenxid and + * relminmxid and heap_truncate_one_rel() in heap.c should handle + * num_tuples and num_pages but that would be slightly inconvenient and + * require an api change. + */ + + /* Ensure RecentXmin is valid -- it almost certainly is but regression + * tests turned up an unlikely corner case where it might not be */ + if (!FirstSnapshotSet) + (void)GetLatestSnapshot(); + Assert(FirstSnapshotSet); + vac_update_relstats(rel, + 0, /* num_pages */ + -1, /* num_tuples */ + 0, /* num_all_visible_pages */ + true, /* hasindex -- ignored due to in_outer_xact false */ + RecentXmin, /* frozenxid */ + RelationIsPermanent(rel) ? GetOldestMultiXactId() : GetOurOldestMultiXactId(), + NULL, NULL, /* frozenxid_updated, minmxid_updated */ + false /* in_outer_xac (see above) */ + ); + RelationTruncate(rel, 0); } diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c index e1191a7564..b06e9ae18d 100644 --- a/src/backend/access/transam/multixact.c +++ b/src/backend/access/transam/multixact.c @@ -2489,6 +2489,21 @@ ExtendMultiXactMember(MultiXactOffset offset, int nmembers) } } +/* + * GetOurOldestMultiXactId + * + * Expose the oldest MultiXactId possibly seen as live by *this* + * transaction. This is mainly useful for initializing relminmxid on temp + * tables since they can't been modified by other transactions. + */ + +MultiXactId +GetOurOldestMultiXactId(void) +{ + MultiXactIdSetOldestVisible(); + return OldestVisibleMXactId[MyBackendId]; +} + /* * GetOldestMultiXactId * diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index bdd413f01b..8d89530e66 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -30,6 +30,7 @@ #include "postgres.h" #include "access/genam.h" +#include "access/heapam.h" #include "access/multixact.h" #include "access/relation.h" #include "access/table.h" @@ -72,6 +73,7 @@ #include "utils/fmgroids.h" #include "utils/inval.h" #include "utils/lsyscache.h" +#include "utils/snapmgr.h" #include "utils/syscache.h" @@ -972,7 +974,9 @@ AddNewRelationTuple(Relation pg_class_desc, */ new_rel_reltup = new_rel_desc->rd_rel; - /* The relation is empty */ + /* The relation is empty + (These are also in heapam_relation_nontransactional_truncate) + */ new_rel_reltup->relpages = 0; new_rel_reltup->reltuples = -1; new_rel_reltup->relallvisible = 0; @@ -2996,6 +3000,14 @@ RelationTruncateIndexes(Relation heapRelation) * This is not transaction-safe! There is another, transaction-safe * implementation in commands/tablecmds.c. We now use this only for * ON COMMIT truncation of temporary tables, where it doesn't matter. + * + * Or whenever a table's relfilenode was created within the same transaction + * such as when the table was created or truncated (normally) within this + * transaction. + * + * The correctness of this code depends on the fact that the table creation or + * truncation would be rolled back *including* the insert/update to the + * pg_class row that we update in place here. */ void heap_truncate(List *relids) diff --git a/src/include/access/multixact.h b/src/include/access/multixact.h index 4cbe17de7b..ca7ebbe3b2 100644 --- a/src/include/access/multixact.h +++ b/src/include/access/multixact.h @@ -139,6 +139,7 @@ extern void MultiXactGetCheckptMulti(bool is_shutdown, MultiXactId *oldestMulti, Oid *oldestMultiDB); extern void CheckPointMultiXact(void); +extern MultiXactId GetOurOldestMultiXactId(void); extern MultiXactId GetOldestMultiXactId(void); extern void TruncateMultiXact(MultiXactId newOldestMulti, Oid newOldestMultiDB); -- 2.38.1