diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c index 6ff9251..bad6bbf 100644 --- a/src/backend/access/heap/pruneheap.c +++ b/src/backend/access/heap/pruneheap.c @@ -99,10 +99,18 @@ heap_page_prune_opt(Relation relation, Buffer buffer) * consumed between this point and acquiring the lock). This allows us to * save significant overhead in the case where the page is found not to be * prunable. + * + * We don't allow early pruning for a TOAST relation because the dangling + * pointer from a heap tuple which might still be visible to someone could + * cause problems. XXX: Since update of TOAST values is not allowed is + * there really sufficient benefit for pruning TOAST pages at all? Should + * we simply return for a TOAST relation? */ if (IsCatalogRelation(relation) || RelationIsAccessibleInLogicalDecoding(relation)) OldestXmin = RecentGlobalXmin; + else if (IsToastRelation(relation)) + OldestXmin = RecentGlobalDataXmin; else OldestXmin = TransactionIdLimitedForOldSnapshots(RecentGlobalDataXmin, diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 43bbd90..2351d84 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -852,7 +852,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose, */ vacuum_set_xid_limits(OldHeap, 0, 0, 0, 0, &OldestXmin, &FreezeXid, NULL, &MultiXactCutoff, - NULL); + NULL, false); /* * FreezeXid will become the table's new relfrozenxid, and that mustn't go diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 0563e63..453a1bc 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -72,7 +72,7 @@ static void vac_truncate_clog(TransactionId frozenXID, TransactionId lastSaneFrozenXid, MultiXactId lastSaneMinMulti); static bool vacuum_rel(Oid relid, RangeVar *relation, int options, - VacuumParams *params); + VacuumParams *params, bool toastRecursion); /* * Primary entry point for manual VACUUM and ANALYZE commands @@ -302,7 +302,7 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params, if (options & VACOPT_VACUUM) { - if (!vacuum_rel(relid, relation, options, params)) + if (!vacuum_rel(relid, relation, options, params, false)) continue; } @@ -479,11 +479,13 @@ vacuum_set_xid_limits(Relation rel, TransactionId *freezeLimit, TransactionId *xidFullScanLimit, MultiXactId *multiXactCutoff, - MultiXactId *mxactFullScanLimit) + MultiXactId *mxactFullScanLimit, + bool noOldestXminAdjustment) { int freezemin; int mxid_freezemin; int effective_multixact_freeze_max_age; + TransactionId xmin; TransactionId limit; TransactionId safeLimit; MultiXactId mxactLimit; @@ -498,8 +500,12 @@ vacuum_set_xid_limits(Relation rel, * working on a particular table at any time, and that each vacuum is * always an independent transaction. */ - *oldestXmin = - TransactionIdLimitedForOldSnapshots(GetOldestXmin(rel, true), rel); + + xmin = GetOldestXmin(rel, true); + if (noOldestXminAdjustment) + *oldestXmin = xmin; + else + *oldestXmin = TransactionIdLimitedForOldSnapshots(xmin, rel); Assert(TransactionIdIsNormal(*oldestXmin)); @@ -1182,10 +1188,21 @@ vac_truncate_clog(TransactionId frozenXID, * many small transactions. Otherwise, two-phase locking would require * us to lock the entire database during one pass of the vacuum cleaner. * + * When old_snapshot_threshold is allowing early pruning/vacuuming, care + * must be taken with cleanup of TOAST relations. While they normally + * can be pruned or vacuumed independently of the owning heap, and MVCC + * visibility rules will keep things safe, the heap must be cleaned up + * before its related TOAST relation to keep things safe. Since a vacuum + * of a heap causes a recursive call here to also vacuumm TOAST data, + * pass along a parameter to show when this is done, so the oldestXmin + * so we can allow the oldestXmin adjustment for TOAST vacuum only when + * it is safe. + * * At entry and exit, we are not inside a transaction. */ static bool -vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params) +vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params, + bool toastRecursion) { LOCKMODE lmode; Relation onerel; @@ -1390,7 +1407,8 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params) (options & VACOPT_VERBOSE) != 0); } else - lazy_vacuum_rel(onerel, options, params, vac_strategy); + lazy_vacuum_rel(onerel, options, params, vac_strategy, + toastRecursion ? false : IsToastRelation(onerel)); /* Roll back any GUC changes executed by index functions */ AtEOXact_GUC(false, save_nestlevel); @@ -1416,7 +1434,7 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params) * totally unimportant for toast relations. */ if (toast_relid != InvalidOid) - vacuum_rel(toast_relid, relation, options, params); + vacuum_rel(toast_relid, relation, options, params, true); /* * Now release the session-level lock on the master table. diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c index 32b6fdd..f24dbfa 100644 --- a/src/backend/commands/vacuumlazy.c +++ b/src/backend/commands/vacuumlazy.c @@ -174,7 +174,7 @@ static bool heap_page_is_all_visible(Relation rel, Buffer buf, */ void lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params, - BufferAccessStrategy bstrategy) + BufferAccessStrategy bstrategy, bool noOldestXminAdjustment) { LVRelStats *vacrelstats; Relation *Irel; @@ -221,7 +221,8 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params, params->multixact_freeze_min_age, params->multixact_freeze_table_age, &OldestXmin, &FreezeLimit, &xidFullScanLimit, - &MultiXactCutoff, &mxactFullScanLimit); + &MultiXactCutoff, &mxactFullScanLimit, + noOldestXminAdjustment); /* * We request an aggressive scan if the table's frozen Xid is now older diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h index 80cd4a8..ca76db6 100644 --- a/src/include/commands/vacuum.h +++ b/src/include/commands/vacuum.h @@ -184,13 +184,15 @@ extern void vacuum_set_xid_limits(Relation rel, TransactionId *freezeLimit, TransactionId *xidFullScanLimit, MultiXactId *multiXactCutoff, - MultiXactId *mxactFullScanLimit); + MultiXactId *mxactFullScanLimit, + bool noOldestXminAdjustment); extern void vac_update_datfrozenxid(void); extern void vacuum_delay_point(void); /* in commands/vacuumlazy.c */ extern void lazy_vacuum_rel(Relation onerel, int options, - VacuumParams *params, BufferAccessStrategy bstrategy); + VacuumParams *params, BufferAccessStrategy bstrategy, + bool noOldestXminAdjustment); /* in commands/analyze.c */ extern void analyze_rel(Oid relid, RangeVar *relation, int options,