Skip to content

Commit 4ce3afb

Browse files
Refactor how VACUUM passes around its XID cutoffs.
Use a dedicated struct for the XID/MXID cutoffs used by VACUUM, such as FreezeLimit and OldestXmin. This state is initialized in vacuum.c, and then passed around by code from vacuumlazy.c to heapam.c freezing related routines. The new convention is that everybody works off of the same cutoff state, which is passed around via pointers to const. Also simplify some of the logic for dealing with frozen xmin in heap_prepare_freeze_tuple: add dedicated "xmin_already_frozen" state to clearly distinguish xmin XIDs that we're going to freeze from those that were already frozen from before. That way the routine's xmin handling code is symmetrical with the existing xmax handling code. This is preparation for an upcoming commit that will add page level freezing. Also refactor the control flow within FreezeMultiXactId(), while adding stricter sanity checks. We now test OldestXmin directly, instead of using FreezeLimit as an inexact proxy for OldestXmin. This is further preparation for the page level freezing work, which will make the function's caller cede control of page level freezing to the function where appropriate (where heap_prepare_freeze_tuple sees a tuple that happens to contain a MultiXactId in its xmax). Author: Peter Geoghegan <pg@bowt.ie> Reviewed-By: Jeff Davis <pgsql@j-davis.com> Discussion: https://postgr.es/m/CAH2-WznS9TxXmz2_=SY+SyJyDFbiOftKofM9=aDo68BbXNBUMA@mail.gmail.com
1 parent e42e312 commit 4ce3afb

File tree

8 files changed

+431
-458
lines changed

8 files changed

+431
-458
lines changed

src/backend/access/heap/heapam.c

Lines changed: 232 additions & 246 deletions
Large diffs are not rendered by default.

src/backend/access/heap/vacuumlazy.c

Lines changed: 81 additions & 115 deletions
Large diffs are not rendered by default.

src/backend/access/transam/multixact.c

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2813,14 +2813,11 @@ ReadMultiXactCounts(uint32 *multixacts, MultiXactOffset *members)
28132813
* As the fraction of the member space currently in use grows, we become
28142814
* more aggressive in clamping this value. That not only causes autovacuum
28152815
* to ramp up, but also makes any manual vacuums the user issues more
2816-
* aggressive. This happens because vacuum_set_xid_limits() clamps the
2817-
* freeze table and the minimum freeze age based on the effective
2816+
* aggressive. This happens because vacuum_get_cutoffs() will clamp the
2817+
* freeze table and the minimum freeze age cutoffs based on the effective
28182818
* autovacuum_multixact_freeze_max_age this function returns. In the worst
28192819
* case, we'll claim the freeze_max_age to zero, and every vacuum of any
2820-
* table will try to freeze every multixact.
2821-
*
2822-
* It's possible that these thresholds should be user-tunable, but for now
2823-
* we keep it simple.
2820+
* table will freeze every multixact.
28242821
*/
28252822
int
28262823
MultiXactMemberFreezeThreshold(void)

src/backend/commands/cluster.c

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -826,10 +826,7 @@ copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
826826
TupleDesc oldTupDesc PG_USED_FOR_ASSERTS_ONLY;
827827
TupleDesc newTupDesc PG_USED_FOR_ASSERTS_ONLY;
828828
VacuumParams params;
829-
TransactionId OldestXmin,
830-
FreezeXid;
831-
MultiXactId OldestMxact,
832-
MultiXactCutoff;
829+
struct VacuumCutoffs cutoffs;
833830
bool use_sort;
834831
double num_tuples = 0,
835832
tups_vacuumed = 0,
@@ -918,23 +915,24 @@ copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
918915
* not to be aggressive about this.
919916
*/
920917
memset(&params, 0, sizeof(VacuumParams));
921-
vacuum_set_xid_limits(OldHeap, &params, &OldestXmin, &OldestMxact,
922-
&FreezeXid, &MultiXactCutoff);
918+
vacuum_get_cutoffs(OldHeap, &params, &cutoffs);
923919

924920
/*
925921
* FreezeXid will become the table's new relfrozenxid, and that mustn't go
926922
* backwards, so take the max.
927923
*/
928924
if (TransactionIdIsValid(OldHeap->rd_rel->relfrozenxid) &&
929-
TransactionIdPrecedes(FreezeXid, OldHeap->rd_rel->relfrozenxid))
930-
FreezeXid = OldHeap->rd_rel->relfrozenxid;
925+
TransactionIdPrecedes(cutoffs.FreezeLimit,
926+
OldHeap->rd_rel->relfrozenxid))
927+
cutoffs.FreezeLimit = OldHeap->rd_rel->relfrozenxid;
931928

932929
/*
933930
* MultiXactCutoff, similarly, shouldn't go backwards either.
934931
*/
935932
if (MultiXactIdIsValid(OldHeap->rd_rel->relminmxid) &&
936-
MultiXactIdPrecedes(MultiXactCutoff, OldHeap->rd_rel->relminmxid))
937-
MultiXactCutoff = OldHeap->rd_rel->relminmxid;
933+
MultiXactIdPrecedes(cutoffs.MultiXactCutoff,
934+
OldHeap->rd_rel->relminmxid))
935+
cutoffs.MultiXactCutoff = OldHeap->rd_rel->relminmxid;
938936

939937
/*
940938
* Decide whether to use an indexscan or seqscan-and-optional-sort to scan
@@ -973,13 +971,14 @@ copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
973971
* values (e.g. because the AM doesn't use freezing).
974972
*/
975973
table_relation_copy_for_cluster(OldHeap, NewHeap, OldIndex, use_sort,
976-
OldestXmin, &FreezeXid, &MultiXactCutoff,
974+
cutoffs.OldestXmin, &cutoffs.FreezeLimit,
975+
&cutoffs.MultiXactCutoff,
977976
&num_tuples, &tups_vacuumed,
978977
&tups_recently_dead);
979978

980979
/* return selected values to caller, get set as relfrozenxid/minmxid */
981-
*pFreezeXid = FreezeXid;
982-
*pCutoffMulti = MultiXactCutoff;
980+
*pFreezeXid = cutoffs.FreezeLimit;
981+
*pCutoffMulti = cutoffs.MultiXactCutoff;
983982

984983
/* Reset rd_toastoid just to be tidy --- it shouldn't be looked at again */
985984
NewHeap->rd_toastoid = InvalidOid;

src/backend/commands/vacuum.c

Lines changed: 55 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -907,34 +907,20 @@ get_all_vacuum_rels(int options)
907907
}
908908

909909
/*
910-
* vacuum_set_xid_limits() -- compute OldestXmin and freeze cutoff points
910+
* vacuum_get_cutoffs() -- compute OldestXmin and freeze cutoff points
911911
*
912912
* The target relation and VACUUM parameters are our inputs.
913913
*
914-
* Our output parameters are:
915-
* - OldestXmin is the Xid below which tuples deleted by any xact (that
916-
* committed) should be considered DEAD, not just RECENTLY_DEAD.
917-
* - OldestMxact is the Mxid below which MultiXacts are definitely not
918-
* seen as visible by any running transaction.
919-
* - FreezeLimit is the Xid below which all Xids are definitely frozen or
920-
* removed during aggressive vacuums.
921-
* - MultiXactCutoff is the value below which all MultiXactIds are definitely
922-
* removed from Xmax during aggressive vacuums.
914+
* Output parameters are the cutoffs that VACUUM caller should use.
923915
*
924916
* Return value indicates if vacuumlazy.c caller should make its VACUUM
925917
* operation aggressive. An aggressive VACUUM must advance relfrozenxid up to
926918
* FreezeLimit (at a minimum), and relminmxid up to MultiXactCutoff (at a
927919
* minimum).
928-
*
929-
* OldestXmin and OldestMxact are the most recent values that can ever be
930-
* passed to vac_update_relstats() as frozenxid and minmulti arguments by our
931-
* vacuumlazy.c caller later on. These values should be passed when it turns
932-
* out that VACUUM will leave no unfrozen XIDs/MXIDs behind in the table.
933920
*/
934921
bool
935-
vacuum_set_xid_limits(Relation rel, const VacuumParams *params,
936-
TransactionId *OldestXmin, MultiXactId *OldestMxact,
937-
TransactionId *FreezeLimit, MultiXactId *MultiXactCutoff)
922+
vacuum_get_cutoffs(Relation rel, const VacuumParams *params,
923+
struct VacuumCutoffs *cutoffs)
938924
{
939925
int freeze_min_age,
940926
multixact_freeze_min_age,
@@ -954,6 +940,10 @@ vacuum_set_xid_limits(Relation rel, const VacuumParams *params,
954940
freeze_table_age = params->freeze_table_age;
955941
multixact_freeze_table_age = params->multixact_freeze_table_age;
956942

943+
/* Set pg_class fields in cutoffs */
944+
cutoffs->relfrozenxid = rel->rd_rel->relfrozenxid;
945+
cutoffs->relminmxid = rel->rd_rel->relminmxid;
946+
957947
/*
958948
* Acquire OldestXmin.
959949
*
@@ -965,14 +955,14 @@ vacuum_set_xid_limits(Relation rel, const VacuumParams *params,
965955
* that only one vacuum process can be working on a particular table at
966956
* any time, and that each vacuum is always an independent transaction.
967957
*/
968-
*OldestXmin = GetOldestNonRemovableTransactionId(rel);
958+
cutoffs->OldestXmin = GetOldestNonRemovableTransactionId(rel);
969959

970960
if (OldSnapshotThresholdActive())
971961
{
972962
TransactionId limit_xmin;
973963
TimestampTz limit_ts;
974964

975-
if (TransactionIdLimitedForOldSnapshots(*OldestXmin, rel,
965+
if (TransactionIdLimitedForOldSnapshots(cutoffs->OldestXmin, rel,
976966
&limit_xmin, &limit_ts))
977967
{
978968
/*
@@ -982,20 +972,48 @@ vacuum_set_xid_limits(Relation rel, const VacuumParams *params,
982972
* frequency), but would still be a significant improvement.
983973
*/
984974
SetOldSnapshotThresholdTimestamp(limit_ts, limit_xmin);
985-
*OldestXmin = limit_xmin;
975+
cutoffs->OldestXmin = limit_xmin;
986976
}
987977
}
988978

989-
Assert(TransactionIdIsNormal(*OldestXmin));
979+
Assert(TransactionIdIsNormal(cutoffs->OldestXmin));
990980

991981
/* Acquire OldestMxact */
992-
*OldestMxact = GetOldestMultiXactId();
993-
Assert(MultiXactIdIsValid(*OldestMxact));
982+
cutoffs->OldestMxact = GetOldestMultiXactId();
983+
Assert(MultiXactIdIsValid(cutoffs->OldestMxact));
994984

995985
/* Acquire next XID/next MXID values used to apply age-based settings */
996986
nextXID = ReadNextTransactionId();
997987
nextMXID = ReadNextMultiXactId();
998988

989+
/*
990+
* Also compute the multixact age for which freezing is urgent. This is
991+
* normally autovacuum_multixact_freeze_max_age, but may be less if we are
992+
* short of multixact member space.
993+
*/
994+
effective_multixact_freeze_max_age = MultiXactMemberFreezeThreshold();
995+
996+
/*
997+
* Almost ready to set freeze output parameters; check if OldestXmin or
998+
* OldestMxact are held back to an unsafe degree before we start on that
999+
*/
1000+
safeOldestXmin = nextXID - autovacuum_freeze_max_age;
1001+
if (!TransactionIdIsNormal(safeOldestXmin))
1002+
safeOldestXmin = FirstNormalTransactionId;
1003+
safeOldestMxact = nextMXID - effective_multixact_freeze_max_age;
1004+
if (safeOldestMxact < FirstMultiXactId)
1005+
safeOldestMxact = FirstMultiXactId;
1006+
if (TransactionIdPrecedes(cutoffs->OldestXmin, safeOldestXmin))
1007+
ereport(WARNING,
1008+
(errmsg("cutoff for removing and freezing tuples is far in the past"),
1009+
errhint("Close open transactions soon to avoid wraparound problems.\n"
1010+
"You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
1011+
if (MultiXactIdPrecedes(cutoffs->OldestMxact, safeOldestMxact))
1012+
ereport(WARNING,
1013+
(errmsg("cutoff for freezing multixacts is far in the past"),
1014+
errhint("Close open transactions soon to avoid wraparound problems.\n"
1015+
"You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
1016+
9991017
/*
10001018
* Determine the minimum freeze age to use: as specified by the caller, or
10011019
* vacuum_freeze_min_age, but in any case not more than half
@@ -1008,19 +1026,12 @@ vacuum_set_xid_limits(Relation rel, const VacuumParams *params,
10081026
Assert(freeze_min_age >= 0);
10091027

10101028
/* Compute FreezeLimit, being careful to generate a normal XID */
1011-
*FreezeLimit = nextXID - freeze_min_age;
1012-
if (!TransactionIdIsNormal(*FreezeLimit))
1013-
*FreezeLimit = FirstNormalTransactionId;
1029+
cutoffs->FreezeLimit = nextXID - freeze_min_age;
1030+
if (!TransactionIdIsNormal(cutoffs->FreezeLimit))
1031+
cutoffs->FreezeLimit = FirstNormalTransactionId;
10141032
/* FreezeLimit must always be <= OldestXmin */
1015-
if (TransactionIdPrecedes(*OldestXmin, *FreezeLimit))
1016-
*FreezeLimit = *OldestXmin;
1017-
1018-
/*
1019-
* Compute the multixact age for which freezing is urgent. This is
1020-
* normally autovacuum_multixact_freeze_max_age, but may be less if we are
1021-
* short of multixact member space.
1022-
*/
1023-
effective_multixact_freeze_max_age = MultiXactMemberFreezeThreshold();
1033+
if (TransactionIdPrecedes(cutoffs->OldestXmin, cutoffs->FreezeLimit))
1034+
cutoffs->FreezeLimit = cutoffs->OldestXmin;
10241035

10251036
/*
10261037
* Determine the minimum multixact freeze age to use: as specified by
@@ -1035,33 +1046,12 @@ vacuum_set_xid_limits(Relation rel, const VacuumParams *params,
10351046
Assert(multixact_freeze_min_age >= 0);
10361047

10371048
/* Compute MultiXactCutoff, being careful to generate a valid value */
1038-
*MultiXactCutoff = nextMXID - multixact_freeze_min_age;
1039-
if (*MultiXactCutoff < FirstMultiXactId)
1040-
*MultiXactCutoff = FirstMultiXactId;
1049+
cutoffs->MultiXactCutoff = nextMXID - multixact_freeze_min_age;
1050+
if (cutoffs->MultiXactCutoff < FirstMultiXactId)
1051+
cutoffs->MultiXactCutoff = FirstMultiXactId;
10411052
/* MultiXactCutoff must always be <= OldestMxact */
1042-
if (MultiXactIdPrecedes(*OldestMxact, *MultiXactCutoff))
1043-
*MultiXactCutoff = *OldestMxact;
1044-
1045-
/*
1046-
* Done setting output parameters; check if OldestXmin or OldestMxact are
1047-
* held back to an unsafe degree in passing
1048-
*/
1049-
safeOldestXmin = nextXID - autovacuum_freeze_max_age;
1050-
if (!TransactionIdIsNormal(safeOldestXmin))
1051-
safeOldestXmin = FirstNormalTransactionId;
1052-
safeOldestMxact = nextMXID - effective_multixact_freeze_max_age;
1053-
if (safeOldestMxact < FirstMultiXactId)
1054-
safeOldestMxact = FirstMultiXactId;
1055-
if (TransactionIdPrecedes(*OldestXmin, safeOldestXmin))
1056-
ereport(WARNING,
1057-
(errmsg("cutoff for removing and freezing tuples is far in the past"),
1058-
errhint("Close open transactions soon to avoid wraparound problems.\n"
1059-
"You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
1060-
if (MultiXactIdPrecedes(*OldestMxact, safeOldestMxact))
1061-
ereport(WARNING,
1062-
(errmsg("cutoff for freezing multixacts is far in the past"),
1063-
errhint("Close open transactions soon to avoid wraparound problems.\n"
1064-
"You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
1053+
if (MultiXactIdPrecedes(cutoffs->OldestMxact, cutoffs->MultiXactCutoff))
1054+
cutoffs->MultiXactCutoff = cutoffs->OldestMxact;
10651055

10661056
/*
10671057
* Finally, figure out if caller needs to do an aggressive VACUUM or not.
@@ -1113,13 +1103,13 @@ vacuum_set_xid_limits(Relation rel, const VacuumParams *params,
11131103
* mechanism to determine if its table's relfrozenxid and relminmxid are now
11141104
* dangerously far in the past.
11151105
*
1116-
* Input parameters are the target relation's relfrozenxid and relminmxid.
1117-
*
11181106
* When we return true, VACUUM caller triggers the failsafe.
11191107
*/
11201108
bool
1121-
vacuum_xid_failsafe_check(TransactionId relfrozenxid, MultiXactId relminmxid)
1109+
vacuum_xid_failsafe_check(const struct VacuumCutoffs *cutoffs)
11221110
{
1111+
TransactionId relfrozenxid = cutoffs->relfrozenxid;
1112+
MultiXactId relminmxid = cutoffs->relminmxid;
11231113
TransactionId xid_skip_limit;
11241114
MultiXactId multi_skip_limit;
11251115
int skip_index_vacuum;

src/include/access/heapam.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838

3939
typedef struct BulkInsertStateData *BulkInsertState;
4040
struct TupleTableSlot;
41+
struct VacuumCutoffs;
4142

4243
#define MaxLockTupleMode LockTupleExclusive
4344

@@ -178,8 +179,7 @@ extern TM_Result heap_lock_tuple(Relation relation, HeapTuple tuple,
178179

179180
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
180181
extern bool heap_prepare_freeze_tuple(HeapTupleHeader tuple,
181-
TransactionId relfrozenxid, TransactionId relminmxid,
182-
TransactionId cutoff_xid, TransactionId cutoff_multi,
182+
const struct VacuumCutoffs *cutoffs,
183183
HeapTupleFreeze *frz, bool *totally_frozen,
184184
TransactionId *relfrozenxid_out,
185185
MultiXactId *relminmxid_out);
@@ -188,9 +188,9 @@ extern void heap_freeze_execute_prepared(Relation rel, Buffer buffer,
188188
HeapTupleFreeze *tuples, int ntuples);
189189
extern bool heap_freeze_tuple(HeapTupleHeader tuple,
190190
TransactionId relfrozenxid, TransactionId relminmxid,
191-
TransactionId cutoff_xid, TransactionId cutoff_multi);
192-
extern bool heap_tuple_would_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid,
193-
MultiXactId cutoff_multi,
191+
TransactionId FreezeLimit, TransactionId MultiXactCutoff);
192+
extern bool heap_tuple_would_freeze(HeapTupleHeader tuple,
193+
const struct VacuumCutoffs *cutoffs,
194194
TransactionId *relfrozenxid_out,
195195
MultiXactId *relminmxid_out);
196196
extern bool heap_tuple_needs_eventual_freeze(HeapTupleHeader tuple);

src/include/access/tableam.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1634,7 +1634,7 @@ table_relation_copy_data(Relation rel, const RelFileLocator *newrlocator)
16341634
* in that index's order; if false and OldIndex is InvalidOid, no sorting is
16351635
* performed
16361636
* - OldIndex - see use_sort
1637-
* - OldestXmin - computed by vacuum_set_xid_limits(), even when
1637+
* - OldestXmin - computed by vacuum_get_cutoffs(), even when
16381638
* not needed for the relation's AM
16391639
* - *xid_cutoff - ditto
16401640
* - *multi_cutoff - ditto

src/include/commands/vacuum.h

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,45 @@ typedef struct VacuumParams
235235
int nworkers;
236236
} VacuumParams;
237237

238+
/*
239+
* VacuumCutoffs is immutable state that describes the cutoffs used by VACUUM.
240+
* Established at the beginning of each VACUUM operation.
241+
*/
242+
struct VacuumCutoffs
243+
{
244+
/*
245+
* Existing pg_class fields at start of VACUUM
246+
*/
247+
TransactionId relfrozenxid;
248+
MultiXactId relminmxid;
249+
250+
/*
251+
* OldestXmin is the Xid below which tuples deleted by any xact (that
252+
* committed) should be considered DEAD, not just RECENTLY_DEAD.
253+
*
254+
* OldestMxact is the Mxid below which MultiXacts are definitely not seen
255+
* as visible by any running transaction.
256+
*
257+
* OldestXmin and OldestMxact are also the most recent values that can
258+
* ever be passed to vac_update_relstats() as frozenxid and minmulti
259+
* arguments at the end of VACUUM. These same values should be passed
260+
* when it turns out that VACUUM will leave no unfrozen XIDs/MXIDs behind
261+
* in the table.
262+
*/
263+
TransactionId OldestXmin;
264+
MultiXactId OldestMxact;
265+
266+
/*
267+
* FreezeLimit is the Xid below which all Xids are definitely frozen or
268+
* removed in pages VACUUM scans and cleanup locks.
269+
*
270+
* MultiXactCutoff is the value below which all MultiXactIds are
271+
* definitely removed from Xmax in pages VACUUM scans and cleanup locks.
272+
*/
273+
TransactionId FreezeLimit;
274+
MultiXactId MultiXactCutoff;
275+
};
276+
238277
/*
239278
* VacDeadItems stores TIDs whose index tuples are deleted by index vacuuming.
240279
*/
@@ -286,13 +325,9 @@ extern void vac_update_relstats(Relation relation,
286325
bool *frozenxid_updated,
287326
bool *minmulti_updated,
288327
bool in_outer_xact);
289-
extern bool vacuum_set_xid_limits(Relation rel, const VacuumParams *params,
290-
TransactionId *OldestXmin,
291-
MultiXactId *OldestMxact,
292-
TransactionId *FreezeLimit,
293-
MultiXactId *MultiXactCutoff);
294-
extern bool vacuum_xid_failsafe_check(TransactionId relfrozenxid,
295-
MultiXactId relminmxid);
328+
extern bool vacuum_get_cutoffs(Relation rel, const VacuumParams *params,
329+
struct VacuumCutoffs *cutoffs);
330+
extern bool vacuum_xid_failsafe_check(const struct VacuumCutoffs *cutoffs);
296331
extern void vac_update_datfrozenxid(void);
297332
extern void vacuum_delay_point(void);
298333
extern bool vacuum_is_permitted_for_relation(Oid relid, Form_pg_class reltuple,

0 commit comments

Comments
 (0)