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.