diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c index 53b1a95254..92389e6666 100644 --- a/src/backend/access/heap/vacuumlazy.c +++ b/src/backend/access/heap/vacuumlazy.c @@ -471,6 +471,7 @@ heap_vacuum_rel(Relation onerel, VacuumParams *params, params->freeze_table_age, params->multixact_freeze_min_age, params->multixact_freeze_table_age, + true, /* we must be a top-level command */ &OldestXmin, &FreezeLimit, &xidFullScanLimit, &MultiXactCutoff, &mxactFullScanLimit); diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 04d12a7ece..0d647e912c 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -67,10 +67,13 @@ typedef struct } RelToCluster; -static void rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose); +static void rebuild_relation(Relation OldHeap, Oid indexOid, + bool isTopLevel, bool verbose); static void copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, - bool verbose, bool *pSwapToastByContent, - TransactionId *pFreezeXid, MultiXactId *pCutoffMulti); + bool isTopLevel, bool verbose, + bool *pSwapToastByContent, + TransactionId *pFreezeXid, + MultiXactId *pCutoffMulti); static List *get_tables_to_cluster(MemoryContext cluster_context); @@ -170,7 +173,7 @@ cluster(ClusterStmt *stmt, bool isTopLevel) table_close(rel, NoLock); /* Do the job. */ - cluster_rel(tableOid, indexOid, stmt->options); + cluster_rel(tableOid, indexOid, stmt->options, isTopLevel); } else { @@ -219,7 +222,8 @@ cluster(ClusterStmt *stmt, bool isTopLevel) PushActiveSnapshot(GetTransactionSnapshot()); /* Do the job. */ cluster_rel(rvtc->tableOid, rvtc->indexOid, - stmt->options | CLUOPT_RECHECK); + stmt->options | CLUOPT_RECHECK, + isTopLevel); PopActiveSnapshot(); CommitTransactionCommand(); } @@ -250,7 +254,7 @@ cluster(ClusterStmt *stmt, bool isTopLevel) * and error messages should refer to the operation as VACUUM not CLUSTER. */ void -cluster_rel(Oid tableOid, Oid indexOid, int options) +cluster_rel(Oid tableOid, Oid indexOid, int options, bool isTopLevel) { Relation OldHeap; bool verbose = ((options & CLUOPT_VERBOSE) != 0); @@ -400,7 +404,7 @@ cluster_rel(Oid tableOid, Oid indexOid, int options) TransferPredicateLocksToHeapRelation(OldHeap); /* rebuild_relation does all the dirty work */ - rebuild_relation(OldHeap, indexOid, verbose); + rebuild_relation(OldHeap, indexOid, isTopLevel, verbose); /* NB: rebuild_relation does table_close() on OldHeap */ @@ -545,11 +549,12 @@ mark_index_clustered(Relation rel, Oid indexOid, bool is_internal) * * OldHeap: table to rebuild --- must be opened and exclusive-locked! * indexOid: index to cluster by, or InvalidOid to rewrite in physical order. + * isTopLevel: should be passed down from ProcessUtility. * * NB: this routine closes OldHeap at the right time; caller should not. */ static void -rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose) +rebuild_relation(Relation OldHeap, Oid indexOid, bool isTopLevel, bool verbose) { Oid tableOid = RelationGetRelid(OldHeap); Oid tableSpace = OldHeap->rd_rel->reltablespace; @@ -577,7 +582,7 @@ rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose) AccessExclusiveLock); /* Copy the heap data into the new table in the desired order */ - copy_table_data(OIDNewHeap, tableOid, indexOid, verbose, + copy_table_data(OIDNewHeap, tableOid, indexOid, isTopLevel, verbose, &swap_toast_by_content, &frozenXid, &cutoffMulti); /* @@ -728,7 +733,8 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, char relpersistence, * *pCutoffMulti receives the MultiXactId used as a cutoff point. */ static void -copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose, +copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, + bool isTopLevel, bool verbose, bool *pSwapToastByContent, TransactionId *pFreezeXid, MultiXactId *pCutoffMulti) { @@ -826,7 +832,7 @@ copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose, * Since we're going to rewrite the whole table anyway, there's no reason * not to be aggressive about this. */ - vacuum_set_xid_limits(OldHeap, 0, 0, 0, 0, + vacuum_set_xid_limits(OldHeap, 0, 0, 0, 0, isTopLevel, &OldestXmin, &FreezeXid, NULL, &MultiXactCutoff, NULL); diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 308a51d95d..ddeec870d8 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -907,6 +907,9 @@ get_all_vacuum_rels(int options) /* * vacuum_set_xid_limits() -- compute oldestXmin and freeze cutoff points * + * Input parameters are the target relation, applicable freeze age settings, + * and isTopLevel which should be passed down from ProcessUtility. + * * The output parameters are: * - oldestXmin is the cutoff value used to distinguish whether tuples are * DEAD or RECENTLY_DEAD (see HeapTupleSatisfiesVacuum). @@ -931,6 +934,7 @@ vacuum_set_xid_limits(Relation rel, int freeze_table_age, int multixact_freeze_min_age, int multixact_freeze_table_age, + bool isTopLevel, TransactionId *oldestXmin, TransactionId *freezeLimit, TransactionId *xidFullScanLimit, @@ -946,32 +950,53 @@ vacuum_set_xid_limits(Relation rel, MultiXactId mxactLimit; MultiXactId safeMxactLimit; - /* - * We can always ignore processes running lazy vacuum. This is because we - * use these values only for deciding which tuples we must keep in the - * tables. Since lazy vacuum doesn't write its XID anywhere (usually no - * XID assigned), it's safe to ignore it. In theory it could be - * problematic to ignore lazy vacuums in a full vacuum, but keep in mind - * that only one vacuum process can be working on a particular table at - * any time, and that each vacuum is always an independent transaction. - */ - *oldestXmin = GetOldestNonRemovableTransactionId(rel); - - if (OldSnapshotThresholdActive()) + if (RELATION_IS_LOCAL(rel) && !IsInTransactionBlock(isTopLevel)) { - TransactionId limit_xmin; - TimestampTz limit_ts; + /* + * If we are processing a temp relation (which by prior checks must be + * one belonging to our session), and we are not inside any + * transaction block, then there can be no tuples in the rel that are + * still in-doubt, nor can there be any that are dead but possibly + * still interesting to some snapshot our session holds. We don't + * need to care whether other sessions could see such tuples, either. + * So we can aggressively set the cutoff xmin to be the nextXid. + */ + *oldestXmin = ReadNewTransactionId(); + } + else + { + /* + * Otherwise, calculate the cutoff xmin normally. + * + * We can always ignore processes running lazy vacuum. This is + * because we use these values only for deciding which tuples we must + * keep in the tables. Since lazy vacuum doesn't write its XID + * anywhere (usually no XID assigned), it's safe to ignore it. In + * theory it could be problematic to ignore lazy vacuums in a full + * vacuum, but keep in mind that only one vacuum process can be + * working on a particular table at any time, and that each vacuum is + * always an independent transaction. + */ + *oldestXmin = GetOldestNonRemovableTransactionId(rel); - if (TransactionIdLimitedForOldSnapshots(*oldestXmin, rel, &limit_xmin, &limit_ts)) + if (OldSnapshotThresholdActive()) { - /* - * TODO: We should only set the threshold if we are pruning on the - * basis of the increased limits. Not as crucial here as it is for - * opportunistic pruning (which often happens at a much higher - * frequency), but would still be a significant improvement. - */ - SetOldSnapshotThresholdTimestamp(limit_ts, limit_xmin); - *oldestXmin = limit_xmin; + TransactionId limit_xmin; + TimestampTz limit_ts; + + if (TransactionIdLimitedForOldSnapshots(*oldestXmin, rel, + &limit_xmin, &limit_ts)) + { + /* + * TODO: We should only set the threshold if we are pruning on + * the basis of the increased limits. Not as crucial here as + * it is for opportunistic pruning (which often happens at a + * much higher frequency), but would still be a significant + * improvement. + */ + SetOldSnapshotThresholdTimestamp(limit_ts, limit_xmin); + *oldestXmin = limit_xmin; + } } } @@ -1905,7 +1930,7 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params) cluster_options |= CLUOPT_VERBOSE; /* VACUUM FULL is now a variant of CLUSTER; see cluster.c */ - cluster_rel(relid, InvalidOid, cluster_options); + cluster_rel(relid, InvalidOid, cluster_options, true); } else table_relation_vacuum(onerel, params, vac_strategy); diff --git a/src/include/commands/cluster.h b/src/include/commands/cluster.h index e05884781b..1eb144204b 100644 --- a/src/include/commands/cluster.h +++ b/src/include/commands/cluster.h @@ -19,7 +19,8 @@ extern void cluster(ClusterStmt *stmt, bool isTopLevel); -extern void cluster_rel(Oid tableOid, Oid indexOid, int options); +extern void cluster_rel(Oid tableOid, Oid indexOid, int options, + bool isTopLevel); extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck, LOCKMODE lockmode); extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h index a4cd721400..d9475c9989 100644 --- a/src/include/commands/vacuum.h +++ b/src/include/commands/vacuum.h @@ -267,6 +267,7 @@ extern void vacuum_set_xid_limits(Relation rel, int freeze_min_age, int freeze_table_age, int multixact_freeze_min_age, int multixact_freeze_table_age, + bool isTopLevel, TransactionId *oldestXmin, TransactionId *freezeLimit, TransactionId *xidFullScanLimit,