poc_skip_index_cleanup_at_anti_wraparound.patch
application/octet-stream
Filename: poc_skip_index_cleanup_at_anti_wraparound.patch
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| src/backend/postmaster/autovacuum.c | 60 | 11 |
| src/backend/utils/misc/guc.c | 2 | 2 |
| src/include/postmaster/autovacuum.h | 6 | 0 |
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 23ef23c13e..113ddf1f5b 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -331,12 +331,14 @@ static autovac_table *table_recheck_autovac(Oid relid, HTAB *table_toast_map,
static void recheck_relation_needs_vacanalyze(Oid relid, AutoVacOpts *avopts,
Form_pg_class classForm,
int effective_multixact_freeze_max_age,
- bool *dovacuum, bool *doanalyze, bool *wraparound);
+ bool *dovacuum, bool *doanalyze, bool *wraparound,
+ bool *skip_index_cleanup);
static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts,
Form_pg_class classForm,
PgStat_StatTabEntry *tabentry,
int effective_multixact_freeze_max_age,
- bool *dovacuum, bool *doanalyze, bool *wraparound);
+ bool *dovacuum, bool *doanalyze, bool *wraparound,
+ bool *skip_index_cleanup);
static void autovacuum_do_vac_analyze(autovac_table *tab,
BufferAccessStrategy bstrategy);
@@ -2080,6 +2082,7 @@ do_autovacuum(void)
bool dovacuum;
bool doanalyze;
bool wraparound;
+ bool skip_index_cleanup;
if (classForm->relkind != RELKIND_RELATION &&
classForm->relkind != RELKIND_MATVIEW)
@@ -2120,7 +2123,8 @@ do_autovacuum(void)
/* Check if it needs vacuum or analyze */
relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
effective_multixact_freeze_max_age,
- &dovacuum, &doanalyze, &wraparound);
+ &dovacuum, &doanalyze, &wraparound,
+ &skip_index_cleanup);
/* Relations that need work are added to table_oids */
if (dovacuum || doanalyze)
@@ -2173,6 +2177,7 @@ do_autovacuum(void)
bool dovacuum;
bool doanalyze;
bool wraparound;
+ bool skip_index_cleanup;
/*
* We cannot safely process other backends' temp tables, so skip 'em.
@@ -2203,7 +2208,8 @@ do_autovacuum(void)
relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
effective_multixact_freeze_max_age,
- &dovacuum, &doanalyze, &wraparound);
+ &dovacuum, &doanalyze, &wraparound,
+ &skip_index_cleanup);
/* ignore analyze for toast tables */
if (dovacuum)
@@ -2801,6 +2807,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
bool doanalyze;
autovac_table *tab = NULL;
bool wraparound;
+ bool skip_index_cleanup;
AutoVacOpts *avopts;
static bool reuse_stats = false;
@@ -2842,7 +2849,8 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
{
recheck_relation_needs_vacanalyze(relid, avopts, classForm,
effective_multixact_freeze_max_age,
- &dovacuum, &doanalyze, &wraparound);
+ &dovacuum, &doanalyze, &wraparound,
+ &skip_index_cleanup);
/* Quick exit if a relation doesn't need to be vacuumed or analyzed */
if (!doanalyze && !dovacuum)
@@ -2857,7 +2865,8 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
recheck_relation_needs_vacanalyze(relid, avopts, classForm,
effective_multixact_freeze_max_age,
- &dovacuum, &doanalyze, &wraparound);
+ &dovacuum, &doanalyze, &wraparound,
+ &skip_index_cleanup);
/* OK, it needs something done */
if (doanalyze || dovacuum)
@@ -2923,7 +2932,9 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
tab->at_params.options = (dovacuum ? VACOPT_VACUUM : 0) |
(doanalyze ? VACOPT_ANALYZE : 0) |
(!wraparound ? VACOPT_SKIP_LOCKED : 0);
- tab->at_params.index_cleanup = VACOPT_TERNARY_DEFAULT;
+ tab->at_params.index_cleanup = (skip_index_cleanup
+ ? VACOPT_TERNARY_DISABLED
+ : VACOPT_TERNARY_DEFAULT);
tab->at_params.truncate = VACOPT_TERNARY_DEFAULT;
/* As of now, we don't support parallel vacuum for autovacuum */
tab->at_params.nworkers = -1;
@@ -2982,7 +2993,8 @@ recheck_relation_needs_vacanalyze(Oid relid,
int effective_multixact_freeze_max_age,
bool *dovacuum,
bool *doanalyze,
- bool *wraparound)
+ bool *wraparound,
+ bool *skip_index_cleanup)
{
PgStat_StatTabEntry *tabentry;
PgStat_StatDBEntry *shared = NULL;
@@ -2999,7 +3011,8 @@ recheck_relation_needs_vacanalyze(Oid relid,
relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
effective_multixact_freeze_max_age,
- dovacuum, doanalyze, wraparound);
+ dovacuum, doanalyze, wraparound,
+ skip_index_cleanup);
/* ignore ANALYZE for toast tables */
if (classForm->relkind == RELKIND_TOASTVALUE)
@@ -3011,7 +3024,8 @@ recheck_relation_needs_vacanalyze(Oid relid,
*
* Check whether a relation needs to be vacuumed or analyzed; return each into
* "dovacuum" and "doanalyze", respectively. Also return whether the vacuum is
- * being forced because of Xid or multixact wraparound.
+ * being forced because of Xid or multixact wraparound and whether or not to skip
+ * index vacuuming.
*
* relopts is a pointer to the AutoVacOpts options (either for itself in the
* case of a plain table, or for either itself or its parent table in the case
@@ -3052,7 +3066,8 @@ relation_needs_vacanalyze(Oid relid,
/* output params below */
bool *dovacuum,
bool *doanalyze,
- bool *wraparound)
+ bool *wraparound,
+ bool *skip_index_cleanup)
{
bool force_vacuum;
bool av_enabled;
@@ -3207,6 +3222,40 @@ relation_needs_vacanalyze(Oid relid,
/* ANALYZE refuses to work with pg_statistic */
if (relid == StatisticRelationId)
*doanalyze = false;
+
+ /*
+ * If a table is at risk of wraparound, we further check if the table's
+ * relfrozenxid is too older than autovacuum_freeze_max_age (more than
+ * autovacuum_freeze_max_age * 1.5 XIDs old). If so, we skip index vacuuming
+ * to quickly complete vacuum operation and advance relfrozenxid.
+ */
+ if (force_vacuum)
+ {
+ TransactionId xidSkipIndCleanupLimit;
+ MultiXactId multiSkipIndCleanupLimit;
+
+ freeze_max_age = Min(freeze_max_age * 1.5, MAX_AUTOVACUUM_FREEZE_MAX_AGE);
+ xidSkipIndCleanupLimit = recentXid - freeze_max_age;
+ if (xidSkipIndCleanupLimit < FirstNormalTransactionId)
+ xidSkipIndCleanupLimit -= FirstNormalTransactionId;
+
+ *skip_index_cleanup = (TransactionIdIsNormal(classForm->relfrozenxid) &&
+ TransactionIdPrecedes(classForm->relfrozenxid,
+ xidSkipIndCleanupLimit));
+
+ if (!(*skip_index_cleanup))
+ {
+ multixact_freeze_max_age = Min(multixact_freeze_max_age * 1.5,
+ MAX_AUTOVACUUM_FREEZE_MAX_AGE);
+ multiSkipIndCleanupLimit = recentMulti - multixact_freeze_max_age;
+ if (multiSkipIndCleanupLimit < FirstMultiXactId)
+ multiSkipIndCleanupLimit -= FirstMultiXactId;
+
+ *skip_index_cleanup = (MultiXactIdIsValid(classForm->relminmxid) &&
+ MultiXactIdPrecedes(classForm->relminmxid,
+ multiSkipIndCleanupLimit));
+ }
+ }
}
/*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 855076b1fd..26ba3fdbbe 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -3197,7 +3197,7 @@ static struct config_int ConfigureNamesInt[] =
},
&autovacuum_freeze_max_age,
/* see pg_resetwal if you change the upper-limit value */
- 200000000, 100000, 2000000000,
+ 200000000, 100000, MAX_AUTOVACUUM_FREEZE_MAX_AGE,
NULL, NULL, NULL
},
{
@@ -3207,7 +3207,7 @@ static struct config_int ConfigureNamesInt[] =
NULL
},
&autovacuum_multixact_freeze_max_age,
- 400000000, 10000, 2000000000,
+ 400000000, 10000, MAX_AUTOVACUUM_FREEZE_MAX_AGE,
NULL, NULL, NULL
},
{
diff --git a/src/include/postmaster/autovacuum.h b/src/include/postmaster/autovacuum.h
index aacdd0f575..e56e0d73ad 100644
--- a/src/include/postmaster/autovacuum.h
+++ b/src/include/postmaster/autovacuum.h
@@ -16,6 +16,12 @@
#include "storage/block.h"
+/*
+ * Maximum value of autovacuum_freeze_max_age and
+ * autovacuum_multixact_freeze_max_age parameters.
+ */
+#define MAX_AUTOVACUUM_FREEZE_MAX_AGE 2000000000
+
/*
* Other processes can request specific work from autovacuum, identified by
* AutoVacuumWorkItem elements.