mirror of
https://github.com/postgres/postgres.git
synced 2025-06-14 18:42:34 +03:00
Centralize horizon determination for temp tables, fixing bug due to skew.
This fixes a bug in the edge case where, for a temp table, heap_page_prune() can end up with a different horizon than heap_vacuum_rel(). Which can trigger errors like "ERROR: cannot freeze committed xmax ...". The bug was introduced due to interaction ofa7212be8b9
"Set cutoff xmin more aggressively when vacuuming a temporary table." withdc7420c2c9
"snapshot scalability: Don't compute global horizons while building snapshots.". The problem is caused by lazy_scan_heap() assuming that the only reason its HeapTupleSatisfiesVacuum() call would return HEAPTUPLE_DEAD is if the tuple is a HOT tuple, or if the tuple's inserting transaction has aborted since the heap_page_prune() call. But aftera7212be8b9
that was also possible in other cases for temp tables, because heap_page_prune() uses a different visibility test afterdc7420c2c9
. The fix is fairly simple: Move the special case logic for temp tables from vacuum_set_xid_limits() to the infrastructure introduced indc7420c2c9
. That ensures that the horizon used for pruning is at least as aggressive as the one used by lazy_scan_heap(). The concrete horizon used for temp tables is slightly different than the logic indc7420c2c9
, but should always be as aggressive as before (see comments). A significant benefit to centralizing the logic procarray.c is that now the more aggressive horizons for temp tables does not just apply to VACUUM but also to e.g. HOT pruning and the nbtree killtuples logic. Because isTopLevel is not needed by vacuum_set_xid_limits() anymore, I undid the the related changes froma7212be8b9
. This commit also adds an isolation test ensuring that the more aggressive vacuuming and pruning of temp tables keeps working. Debugged-By: Amit Kapila <amit.kapila16@gmail.com> Debugged-By: Tom Lane <tgl@sss.pgh.pa.us> Debugged-By: Ashutosh Sharma <ashu.coek88@gmail.com> Author: Andres Freund <andres@anarazel.de> Discussion: https://postgr.es/m/20201014203103.72oke6hqywcyhx7s@alap3.anarazel.de Discussion: https://postgr.es/m/20201015083735.derdzysdtqdvxshp@alap3.anarazel.de
This commit is contained in:
@ -67,13 +67,10 @@ typedef struct
|
||||
} RelToCluster;
|
||||
|
||||
|
||||
static void rebuild_relation(Relation OldHeap, Oid indexOid,
|
||||
bool isTopLevel, bool verbose);
|
||||
static void rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose);
|
||||
static void copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
|
||||
bool isTopLevel, bool verbose,
|
||||
bool *pSwapToastByContent,
|
||||
TransactionId *pFreezeXid,
|
||||
MultiXactId *pCutoffMulti);
|
||||
bool verbose, bool *pSwapToastByContent,
|
||||
TransactionId *pFreezeXid, MultiXactId *pCutoffMulti);
|
||||
static List *get_tables_to_cluster(MemoryContext cluster_context);
|
||||
|
||||
|
||||
@ -173,7 +170,7 @@ cluster(ClusterStmt *stmt, bool isTopLevel)
|
||||
table_close(rel, NoLock);
|
||||
|
||||
/* Do the job. */
|
||||
cluster_rel(tableOid, indexOid, stmt->options, isTopLevel);
|
||||
cluster_rel(tableOid, indexOid, stmt->options);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -222,8 +219,7 @@ cluster(ClusterStmt *stmt, bool isTopLevel)
|
||||
PushActiveSnapshot(GetTransactionSnapshot());
|
||||
/* Do the job. */
|
||||
cluster_rel(rvtc->tableOid, rvtc->indexOid,
|
||||
stmt->options | CLUOPT_RECHECK,
|
||||
isTopLevel);
|
||||
stmt->options | CLUOPT_RECHECK);
|
||||
PopActiveSnapshot();
|
||||
CommitTransactionCommand();
|
||||
}
|
||||
@ -254,7 +250,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, bool isTopLevel)
|
||||
cluster_rel(Oid tableOid, Oid indexOid, int options)
|
||||
{
|
||||
Relation OldHeap;
|
||||
bool verbose = ((options & CLUOPT_VERBOSE) != 0);
|
||||
@ -404,7 +400,7 @@ cluster_rel(Oid tableOid, Oid indexOid, int options, bool isTopLevel)
|
||||
TransferPredicateLocksToHeapRelation(OldHeap);
|
||||
|
||||
/* rebuild_relation does all the dirty work */
|
||||
rebuild_relation(OldHeap, indexOid, isTopLevel, verbose);
|
||||
rebuild_relation(OldHeap, indexOid, verbose);
|
||||
|
||||
/* NB: rebuild_relation does table_close() on OldHeap */
|
||||
|
||||
@ -549,12 +545,11 @@ 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 isTopLevel, bool verbose)
|
||||
rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose)
|
||||
{
|
||||
Oid tableOid = RelationGetRelid(OldHeap);
|
||||
Oid tableSpace = OldHeap->rd_rel->reltablespace;
|
||||
@ -582,7 +577,7 @@ rebuild_relation(Relation OldHeap, Oid indexOid, bool isTopLevel, bool verbose)
|
||||
AccessExclusiveLock);
|
||||
|
||||
/* Copy the heap data into the new table in the desired order */
|
||||
copy_table_data(OIDNewHeap, tableOid, indexOid, isTopLevel, verbose,
|
||||
copy_table_data(OIDNewHeap, tableOid, indexOid, verbose,
|
||||
&swap_toast_by_content, &frozenXid, &cutoffMulti);
|
||||
|
||||
/*
|
||||
@ -733,8 +728,7 @@ 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 isTopLevel, bool verbose,
|
||||
copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
|
||||
bool *pSwapToastByContent, TransactionId *pFreezeXid,
|
||||
MultiXactId *pCutoffMulti)
|
||||
{
|
||||
@ -832,7 +826,7 @@ copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
|
||||
* 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, isTopLevel,
|
||||
vacuum_set_xid_limits(OldHeap, 0, 0, 0, 0,
|
||||
&OldestXmin, &FreezeXid, NULL, &MultiXactCutoff,
|
||||
NULL);
|
||||
|
||||
|
Reference in New Issue
Block a user