Skip to content

Commit f54106f

Browse files
committed
Fix full-table-vacuum request mechanism for MultiXactIds
While autovacuum dutifully launched anti-multixact-wraparound vacuums when the multixact "age" was reached, the vacuum code was not aware that it needed to make them be full table vacuums. As the resulting partial-table vacuums aren't capable of actually increasing relminmxid, autovacuum continued to launch anti-wraparound vacuums that didn't have the intended effect, until age of relfrozenxid caused the vacuum to finally be a full table one via vacuum_freeze_table_age. To fix, introduce logic for multixacts similar to that for plain TransactionIds, using the same GUCs. Backpatch to 9.3, where permanent MultiXactIds were introduced. Andres Freund, some cleanup by Álvaro
1 parent 76a31c6 commit f54106f

File tree

6 files changed

+87
-28
lines changed

6 files changed

+87
-28
lines changed

src/backend/access/transam/multixact.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2374,6 +2374,21 @@ MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2)
23742374
return (diff < 0);
23752375
}
23762376

2377+
/*
2378+
* MultiXactIdPrecedesOrEquals -- is multi1 logically <= multi2?
2379+
*
2380+
* XXX do we need to do something special for InvalidMultiXactId?
2381+
* (Doesn't look like it.)
2382+
*/
2383+
bool
2384+
MultiXactIdPrecedesOrEquals(MultiXactId multi1, MultiXactId multi2)
2385+
{
2386+
int32 diff = (int32) (multi1 - multi2);
2387+
2388+
return (diff <= 0);
2389+
}
2390+
2391+
23772392
/*
23782393
* Decide which of two offsets is earlier.
23792394
*/

src/backend/commands/cluster.c

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,10 @@ cluster(ClusterStmt *stmt, bool isTopLevel)
176176
/* close relation, keep lock till commit */
177177
heap_close(rel, NoLock);
178178

179-
/* Do the job */
179+
/*
180+
* Do the job. We use a -1 freeze_min_age to avoid having CLUSTER
181+
* freeze tuples earlier than a plain VACUUM would.
182+
*/
180183
cluster_rel(tableOid, indexOid, false, stmt->verbose, -1, -1);
181184
}
182185
else
@@ -226,6 +229,7 @@ cluster(ClusterStmt *stmt, bool isTopLevel)
226229
StartTransactionCommand();
227230
/* functions in indexes may want a snapshot set */
228231
PushActiveSnapshot(GetTransactionSnapshot());
232+
/* Do the job. As above, use a -1 freeze_min_age. */
229233
cluster_rel(rvtc->tableOid, rvtc->indexOid, true, stmt->verbose,
230234
-1, -1);
231235
PopActiveSnapshot();
@@ -853,13 +857,12 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
853857
*pSwapToastByContent = false;
854858

855859
/*
856-
* compute xids used to freeze and weed out dead tuples. We use -1
857-
* freeze_min_age to avoid having CLUSTER freeze tuples earlier than a
858-
* plain VACUUM would.
860+
* compute xids used to freeze and weed out dead tuples.
859861
*/
860862
vacuum_set_xid_limits(freeze_min_age, freeze_table_age,
861863
OldHeap->rd_rel->relisshared,
862-
&OldestXmin, &FreezeXid, NULL, &MultiXactCutoff);
864+
&OldestXmin, &FreezeXid, NULL, &MultiXactCutoff,
865+
NULL);
863866

864867
/*
865868
* FreezeXid will become the table's new relfrozenxid, and that mustn't go

src/backend/commands/vacuum.c

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -376,19 +376,39 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
376376

377377
/*
378378
* vacuum_set_xid_limits() -- compute oldest-Xmin and freeze cutoff points
379+
*
380+
* The output parameters are:
381+
* - oldestXmin is the cutoff value used to distinguish whether tuples are
382+
* DEAD or RECENTLY_DEAD (see HeapTupleSatisfiesVacuum).
383+
* - freezeLimit is the Xid below which all Xids are replaced by
384+
* FrozenTransactionId during vacuum.
385+
* - xidFullScanLimit (computed from the the table_freeze_age parameter)
386+
* represents a minimum Xid value; a table whose relfrozenxid is older than
387+
* this will have a full-table vacuum applied to it, to freeze tuples across
388+
* the whole table. Vacuuming a table younger than this value can use a
389+
* partial scan.
390+
* - multiXactCutoff is the value below which all MultiXactIds are removed from
391+
* Xmax.
392+
* - mxactFullScanLimit is a value against which a table's relminmxid value is
393+
* compared to produce a full-table vacuum, as with xidFullScanLimit.
394+
*
395+
* xidFullScanLimit and mxactFullScanLimit can be passed as NULL if caller is
396+
* not interested.
379397
*/
380398
void
381399
vacuum_set_xid_limits(int freeze_min_age,
382400
int freeze_table_age,
383401
bool sharedRel,
384402
TransactionId *oldestXmin,
385403
TransactionId *freezeLimit,
386-
TransactionId *freezeTableLimit,
387-
MultiXactId *multiXactCutoff)
404+
TransactionId *xidFullScanLimit,
405+
MultiXactId *multiXactCutoff,
406+
MultiXactId *mxactFullScanLimit)
388407
{
389408
int freezemin;
390409
TransactionId limit;
391410
TransactionId safeLimit;
411+
MultiXactId mxactLimit;
392412

393413
/*
394414
* We can always ignore processes running lazy vacuum. This is because we
@@ -441,10 +461,22 @@ vacuum_set_xid_limits(int freeze_min_age,
441461

442462
*freezeLimit = limit;
443463

444-
if (freezeTableLimit != NULL)
464+
/*
465+
* simplistic MultiXactId removal limit: use the same policy as for
466+
* freezing Xids (except we use the oldest known mxact instead of the
467+
* current next value).
468+
*/
469+
mxactLimit = GetOldestMultiXactId() - freezemin;
470+
if (mxactLimit < FirstMultiXactId)
471+
mxactLimit = FirstMultiXactId;
472+
*multiXactCutoff = mxactLimit;
473+
474+
if (xidFullScanLimit != NULL)
445475
{
446476
int freezetable;
447477

478+
Assert(mxactFullScanLimit != NULL);
479+
448480
/*
449481
* Determine the table freeze age to use: as specified by the caller,
450482
* or vacuum_freeze_table_age, but in any case not more than
@@ -459,29 +491,25 @@ vacuum_set_xid_limits(int freeze_min_age,
459491
Assert(freezetable >= 0);
460492

461493
/*
462-
* Compute the cutoff XID, being careful not to generate a "permanent"
463-
* XID.
494+
* Compute XID limit causing a full-table vacuum, being careful not to
495+
* generate a "permanent" XID.
464496
*/
465497
limit = ReadNewTransactionId() - freezetable;
466498
if (!TransactionIdIsNormal(limit))
467499
limit = FirstNormalTransactionId;
468500

469-
*freezeTableLimit = limit;
470-
}
471-
472-
if (multiXactCutoff != NULL)
473-
{
474-
MultiXactId mxLimit;
501+
*xidFullScanLimit = limit;
475502

476503
/*
477-
* simplistic multixactid freezing: use the same freezing policy as
478-
* for Xids
504+
* Compute MultiXactId limit to cause a full-table vacuum, being
505+
* careful not to generate an invalid multi. We just copy the logic
506+
* (and limits) from plain XIDs here.
479507
*/
480-
mxLimit = GetOldestMultiXactId() - freezemin;
481-
if (mxLimit < FirstMultiXactId)
482-
mxLimit = FirstMultiXactId;
508+
mxactLimit = ReadNextMultiXactId() - freezetable;
509+
if (mxactLimit < FirstMultiXactId)
510+
mxactLimit = FirstMultiXactId;
483511

484-
*multiXactCutoff = mxLimit;
512+
*mxactFullScanLimit = mxactLimit;
485513
}
486514
}
487515

src/backend/commands/vacuumlazy.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,8 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
180180
write_rate;
181181
bool scan_all; /* should we scan all pages? */
182182
bool scanned_all; /* did we actually scan all pages? */
183-
TransactionId freezeTableLimit;
183+
TransactionId xidFullScanLimit;
184+
MultiXactId mxactFullScanLimit;
184185
BlockNumber new_rel_pages;
185186
double new_rel_tuples;
186187
BlockNumber new_rel_allvisible;
@@ -203,10 +204,19 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
203204

204205
vacuum_set_xid_limits(vacstmt->freeze_min_age, vacstmt->freeze_table_age,
205206
onerel->rd_rel->relisshared,
206-
&OldestXmin, &FreezeLimit, &freezeTableLimit,
207-
&MultiXactCutoff);
207+
&OldestXmin, &FreezeLimit, &xidFullScanLimit,
208+
&MultiXactCutoff, &mxactFullScanLimit);
209+
210+
/*
211+
* We request a full scan if either the table's frozen Xid is now older
212+
* than or equal to the requested Xid full-table scan limit; or if the
213+
* table's minimum MultiXactId is older than or equal to the requested mxid
214+
* full-table scan limit.
215+
*/
208216
scan_all = TransactionIdPrecedesOrEquals(onerel->rd_rel->relfrozenxid,
209-
freezeTableLimit);
217+
xidFullScanLimit);
218+
scan_all |= MultiXactIdPrecedesOrEquals(onerel->rd_rel->relminmxid,
219+
mxactFullScanLimit);
210220

211221
vacrelstats = (LVRelStats *) palloc0(sizeof(LVRelStats));
212222

src/include/access/multixact.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ extern void MultiXactIdSetOldestMember(void);
8787
extern int GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **xids,
8888
bool allow_old);
8989
extern bool MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2);
90+
extern bool MultiXactIdPrecedesOrEquals(MultiXactId multi1,
91+
MultiXactId multi2);
9092

9193
extern void AtEOXact_MultiXact(void);
9294
extern void AtPrepare_MultiXact(void);

src/include/commands/vacuum.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,9 @@ extern void vacuum_set_xid_limits(int freeze_min_age, int freeze_table_age,
159159
bool sharedRel,
160160
TransactionId *oldestXmin,
161161
TransactionId *freezeLimit,
162-
TransactionId *freezeTableLimit,
163-
MultiXactId *multiXactCutoff);
162+
TransactionId *xidFullScanLimit,
163+
MultiXactId *multiXactCutoff,
164+
MultiXactId *mxactFullScanLimit);
164165
extern void vac_update_datfrozenxid(void);
165166
extern void vacuum_delay_point(void);
166167

0 commit comments

Comments
 (0)