Content-Length: 821476 | pFad | http://github.com/postgrespro/postgres/commit/3ecab37d97ed72bf81fd042dcef10e7111369875

97 Teach autovacuum about multixact member wraparound. · postgrespro/postgres@3ecab37 · GitHub
Skip to content

Commit 3ecab37

Browse files
committed
Teach autovacuum about multixact member wraparound.
The logic introduced in commit b69bf30 and repaired in commits 669c7d2 and 7be47c5 helps to ensure that we don't overwrite old multixact member information while it is still needed, but a user who creates many large multixacts can still exhaust the member space (and thus start getting errors) while autovacuum stands idly by. To fix this, progressively ramp down the effective value (but not the actual contents) of autovacuum_multixact_freeze_max_age as member space utilization increases. This makes autovacuum more aggressive and also reduces the threshold for a manual VACUUM to perform a full-table scan. This patch leaves unsolved the problem of ensuring that emergency autovacuums are triggered even when autovacuum=off. We'll need to fix that via a separate patch. Thomas Munro and Robert Haas
1 parent 32c50af commit 3ecab37

File tree

5 files changed

+130
-11
lines changed

5 files changed

+130
-11
lines changed

doc/src/sgml/maintenance.sgml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,9 @@ HINT: Stop the postmaster and vacuum that database in single-user mode.
628628
Like transaction IDs, multixact IDs are implemented as a
629629
32-bit counter and corresponding storage, all of which requires
630630
careful aging management, storage cleanup, and wraparound handling.
631+
There is a separate storage area which holds the list of members in
632+
each multixact, which also uses a 32-bit counter and which must also
633+
be managed.
631634
</para>
632635

633636
<para>
@@ -651,7 +654,10 @@ HINT: Stop the postmaster and vacuum that database in single-user mode.
651654
As a safety device, a whole-table vacuum scan will occur for any table
652655
whose multixact-age is greater than
653656
<xref linkend="guc-autovacuum-multixact-freeze-max-age">.
654-
This will occur even if autovacuum is nominally disabled.
657+
This will occur even if autovacuum is nominally disabled. Whole-table
658+
vacuum scans will also occur progressively for all tables, starting with
659+
those that have the oldest multixact-age, if the amount of used member
660+
storage space exceeds the amount 25% of the addressible storage space.
655661
</para>
656662
</sect3>
657663
</sect2>

src/backend/access/transam/multixact.c

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,11 @@
166166
(MXOffsetToFlagsOffset(xid) + MULTIXACT_FLAGBYTES_PER_GROUP + \
167167
((xid) % MULTIXACT_MEMBERS_PER_MEMBERGROUP) * sizeof(TransactionId))
168168

169+
/* Multixact members wraparound thresholds. */
170+
#define MULTIXACT_MEMBER_SAFE_THRESHOLD (MaxMultiXactOffset / 4)
171+
#define MULTIXACT_MEMBER_DANGER_THRESHOLD \
172+
(MaxMultiXactOffset - MaxMultiXactOffset / 4)
173+
169174

170175
/*
171176
* Links to shared-memory data structures for MultiXact control
@@ -2597,6 +2602,89 @@ find_multixact_start(MultiXactId multi)
25972602
return offset;
25982603
}
25992604

2605+
/*
2606+
* Determine how many multixacts, and how many multixact members, currently
2607+
* exist.
2608+
*/
2609+
static void
2610+
ReadMultiXactCounts(uint32 *multixacts, MultiXactOffset *members)
2611+
{
2612+
MultiXactOffset nextOffset;
2613+
MultiXactOffset oldestOffset;
2614+
MultiXactId oldestMultiXactId;
2615+
MultiXactId nextMultiXactId;
2616+
2617+
LWLockAcquire(MultiXactGenLock, LW_SHARED);
2618+
nextOffset = MultiXactState->nextOffset;
2619+
oldestMultiXactId = MultiXactState->oldestMultiXactId;
2620+
nextMultiXactId = MultiXactState->nextMXact;
2621+
LWLockRelease(MultiXactGenLock);
2622+
2623+
oldestOffset = find_multixact_start(oldestMultiXactId);
2624+
*members = nextOffset - oldestOffset;
2625+
*multixacts = nextMultiXactId - oldestMultiXactId;
2626+
}
2627+
2628+
/*
2629+
* Multixact members can be removed once the multixacts that refer to them
2630+
* are older than every datminxmid. autovacuum_multixact_freeze_max_age and
2631+
* vacuum_multixact_freeze_table_age work together to make sure we never have
2632+
* too many multixacts; we hope that, at least under normal circumstances,
2633+
* this will also be sufficient to keep us from using too many offsets.
2634+
* However, if the average multixact has many members, we might exhaust the
2635+
* members space while still using few enough members that these limits fail
2636+
* to trigger full table scans for relminmxid advancement. At that point,
2637+
* we'd have no choice but to start failing multixact-creating operations
2638+
* with an error.
2639+
*
2640+
* To prevent that, if more than a threshold portion of the members space is
2641+
* used, we effectively reduce autovacuum_multixact_freeze_max_age and
2642+
* to a value just less than the number of multixacts in use. We hope that
2643+
* this will quickly trigger autovacuuming on the table or tables with the
2644+
* oldest relminmxid, thus allowing datminmxid values to advance and removing
2645+
* some members.
2646+
*
2647+
* As the fraction of the member space currently in use grows, we become
2648+
* more aggressive in clamping this value. That not only causes autovacuum
2649+
* to ramp up, but also makes any manual vacuums the user issues more
2650+
* aggressive. This happens because vacuum_set_xid_limits() clamps the
2651+
* freeze table and and the minimum freeze age based on the effective
2652+
* autovacuum_multixact_freeze_max_age this function returns. In the worst
2653+
* case, we'll claim the freeze_max_age to zero, and every vacuum of any
2654+
* table will try to freeze every multixact.
2655+
*
2656+
* It's possible that these thresholds should be user-tunable, but for now
2657+
* we keep it simple.
2658+
*/
2659+
int
2660+
MultiXactMemberFreezeThreshold(void)
2661+
{
2662+
MultiXactOffset members;
2663+
uint32 multixacts;
2664+
uint32 victim_multixacts;
2665+
double fraction;
2666+
2667+
ReadMultiXactCounts(&multixacts, &members);
2668+
2669+
/* If member space utilization is low, no special action is required. */
2670+
if (members <= MULTIXACT_MEMBER_SAFE_THRESHOLD)
2671+
return autovacuum_multixact_freeze_max_age;
2672+
2673+
/*
2674+
* Compute a target for relminmxid advancement. The number of multixacts
2675+
* we try to eliminate from the system is based on how far we are past
2676+
* MULTIXACT_MEMBER_SAFE_THRESHOLD.
2677+
*/
2678+
fraction = (double) (members - MULTIXACT_MEMBER_SAFE_THRESHOLD) /
2679+
(MULTIXACT_MEMBER_DANGER_THRESHOLD - MULTIXACT_MEMBER_SAFE_THRESHOLD);
2680+
victim_multixacts = multixacts * fraction;
2681+
2682+
/* fraction could be > 1.0, but lowest possible freeze age is zero */
2683+
if (victim_multixacts > multixacts)
2684+
return 0;
2685+
return multixacts - victim_multixacts;
2686+
}
2687+
26002688
/*
26012689
* SlruScanDirectory callback.
26022690
* This callback deletes segments that are outside the range determined by

src/backend/commands/vacuum.c

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,7 @@ vacuum_set_xid_limits(Relation rel,
426426
{
427427
int freezemin;
428428
int mxid_freezemin;
429+
int effective_multixact_freeze_max_age;
429430
TransactionId limit;
430431
TransactionId safeLimit;
431432
MultiXactId mxactLimit;
@@ -482,17 +483,24 @@ vacuum_set_xid_limits(Relation rel,
482483

483484
*freezeLimit = limit;
484485

486+
/*
487+
* Compute the multixact age for which freezing is urgent. This is
488+
* normally autovacuum_multixact_freeze_max_age, but may be less if we
489+
* are short of multixact member space.
490+
*/
491+
effective_multixact_freeze_max_age = MultiXactMemberFreezeThreshold();
492+
485493
/*
486494
* Determine the minimum multixact freeze age to use: as specified by
487495
* caller, or vacuum_multixact_freeze_min_age, but in any case not more
488-
* than half autovacuum_multixact_freeze_max_age, so that autovacuums to
496+
* than half effective_multixact_freeze_max_age, so that autovacuums to
489497
* prevent MultiXact wraparound won't occur too frequently.
490498
*/
491499
mxid_freezemin = multixact_freeze_min_age;
492500
if (mxid_freezemin < 0)
493501
mxid_freezemin = vacuum_multixact_freeze_min_age;
494502
mxid_freezemin = Min(mxid_freezemin,
495-
autovacuum_multixact_freeze_max_age / 2);
503+
effective_multixact_freeze_max_age / 2);
496504
Assert(mxid_freezemin >= 0);
497505

498506
/* compute the cutoff multi, being careful to generate a valid value */
@@ -501,7 +509,7 @@ vacuum_set_xid_limits(Relation rel,
501509
mxactLimit = FirstMultiXactId;
502510

503511
safeMxactLimit =
504-
ReadNextMultiXactId() - autovacuum_multixact_freeze_max_age;
512+
ReadNextMultiXactId() - effective_multixact_freeze_max_age;
505513
if (safeMxactLimit < FirstMultiXactId)
506514
safeMxactLimit = FirstMultiXactId;
507515

@@ -556,7 +564,7 @@ vacuum_set_xid_limits(Relation rel,
556564
if (freezetable < 0)
557565
freezetable = vacuum_multixact_freeze_table_age;
558566
freezetable = Min(freezetable,
559-
autovacuum_multixact_freeze_max_age * 0.95);
567+
effective_multixact_freeze_max_age * 0.95);
560568
Assert(freezetable >= 0);
561569

562570
/*

src/backend/postmaster/autovacuum.c

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -303,10 +303,12 @@ static void do_autovacuum(void);
303303
static void FreeWorkerInfo(int code, Datum arg);
304304

305305
static autovac_table *table_recheck_autovac(Oid relid, HTAB *table_toast_map,
306-
TupleDesc pg_class_desc);
306+
TupleDesc pg_class_desc,
307+
int effective_multixact_freeze_max_age);
307308
static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts,
308309
Form_pg_class classForm,
309310
PgStat_StatTabEntry *tabentry,
311+
int effective_multixact_freeze_max_age,
310312
bool *dovacuum, bool *doanalyze, bool *wraparound);
311313

312314
static void autovacuum_do_vac_analyze(autovac_table *tab,
@@ -1147,7 +1149,7 @@ do_start_worker(void)
11471149

11481150
/* Also determine the oldest datminmxid we will consider. */
11491151
recentMulti = ReadNextMultiXactId();
1150-
multiForceLimit = recentMulti - autovacuum_multixact_freeze_max_age;
1152+
multiForceLimit = recentMulti - MultiXactMemberFreezeThreshold();
11511153
if (multiForceLimit < FirstMultiXactId)
11521154
multiForceLimit -= FirstMultiXactId;
11531155

@@ -1936,6 +1938,7 @@ do_autovacuum(void)
19361938
BufferAccessStrategy bstrategy;
19371939
ScanKeyData key;
19381940
TupleDesc pg_class_desc;
1941+
int effective_multixact_freeze_max_age;
19391942

19401943
/*
19411944
* StartTransactionCommand and CommitTransactionCommand will automatically
@@ -1965,6 +1968,13 @@ do_autovacuum(void)
19651968
*/
19661969
pgstat_vacuum_stat();
19671970

1971+
/*
1972+
* Compute the multixact age for which freezing is urgent. This is
1973+
* normally autovacuum_multixact_freeze_max_age, but may be less if we
1974+
* are short of multixact member space.
1975+
*/
1976+
effective_multixact_freeze_max_age = MultiXactMemberFreezeThreshold();
1977+
19681978
/*
19691979
* Find the pg_database entry and select the default freeze ages. We use
19701980
* zero in template and nonconnectable databases, else the system-wide
@@ -2057,6 +2067,7 @@ do_autovacuum(void)
20572067

20582068
/* Check if it needs vacuum or analyze */
20592069
relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
2070+
effective_multixact_freeze_max_age,
20602071
&dovacuum, &doanalyze, &wraparound);
20612072

20622073
/*
@@ -2185,6 +2196,7 @@ do_autovacuum(void)
21852196
shared, dbentry);
21862197

21872198
relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
2199+
effective_multixact_freeze_max_age,
21882200
&dovacuum, &doanalyze, &wraparound);
21892201

21902202
/* ignore analyze for toast tables */
@@ -2275,7 +2287,8 @@ do_autovacuum(void)
22752287
* the race condition is not closed but it is very small.
22762288
*/
22772289
MemoryContextSwitchTo(AutovacMemCxt);
2278-
tab = table_recheck_autovac(relid, table_toast_map, pg_class_desc);
2290+
tab = table_recheck_autovac(relid, table_toast_map, pg_class_desc,
2291+
effective_multixact_freeze_max_age);
22792292
if (tab == NULL)
22802293
{
22812294
/* someone else vacuumed the table, or it went away */
@@ -2482,7 +2495,8 @@ get_pgstat_tabentry_relid(Oid relid, bool isshared, PgStat_StatDBEntry *shared,
24822495
*/
24832496
static autovac_table *
24842497
table_recheck_autovac(Oid relid, HTAB *table_toast_map,
2485-
TupleDesc pg_class_desc)
2498+
TupleDesc pg_class_desc,
2499+
int effective_multixact_freeze_max_age)
24862500
{
24872501
Form_pg_class classForm;
24882502
HeapTuple classTup;
@@ -2528,6 +2542,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
25282542
shared, dbentry);
25292543

25302544
relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
2545+
effective_multixact_freeze_max_age,
25312546
&dovacuum, &doanalyze, &wraparound);
25322547

25332548
/* ignore ANALYZE for toast tables */
@@ -2655,6 +2670,7 @@ relation_needs_vacanalyze(Oid relid,
26552670
AutoVacOpts *relopts,
26562671
Form_pg_class classForm,
26572672
PgStat_StatTabEntry *tabentry,
2673+
int effective_multixact_freeze_max_age,
26582674
/* output params below */
26592675
bool *dovacuum,
26602676
bool *doanalyze,
@@ -2715,8 +2731,8 @@ relation_needs_vacanalyze(Oid relid,
27152731
: autovacuum_freeze_max_age;
27162732

27172733
multixact_freeze_max_age = (relopts && relopts->multixact_freeze_max_age >= 0)
2718-
? Min(relopts->multixact_freeze_max_age, autovacuum_multixact_freeze_max_age)
2719-
: autovacuum_multixact_freeze_max_age;
2734+
? Min(relopts->multixact_freeze_max_age, effective_multixact_freeze_max_age)
2735+
: effective_multixact_freeze_max_age;
27202736

27212737
av_enabled = (relopts ? relopts->enabled : true);
27222738

src/include/access/multixact.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ extern void MultiXactAdvanceNextMXact(MultiXactId minMulti,
126126
MultiXactOffset minMultiOffset);
127127
extern void MultiXactAdvanceOldest(MultiXactId oldestMulti, Oid oldestMultiDB);
128128
extern void MultiXactSetSafeTruncate(MultiXactId safeTruncateMulti);
129+
extern int MultiXactMemberFreezeThreshold(void);
129130

130131
extern void multixact_twophase_recover(TransactionId xid, uint16 info,
131132
void *recdata, uint32 len);

0 commit comments

Comments
 (0)








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/postgrespro/postgres/commit/3ecab37d97ed72bf81fd042dcef10e7111369875

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy