v3-0003-Skip-index-vacuuming-when-there-is-a-risk-of-wrap.patch
application/octet-stream
Filename: v3-0003-Skip-index-vacuuming-when-there-is-a-risk-of-wrap.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: format-patch
Series: patch v3-0003
Subject: Skip index vacuuming when there is a risk of wraparound.
| File | + | − |
|---|---|---|
| src/backend/access/heap/vacuumlazy.c | 81 | 11 |
| src/backend/utils/misc/guc.c | 2 | 2 |
| src/include/postmaster/autovacuum.h | 6 | 0 |
From 6d227af53823866b56cbb7932a7d8e4f21f764d0 Mon Sep 17 00:00:00 2001
From: Masahiko Sawada <sawada.mshk@gmail.com>
Date: Thu, 18 Mar 2021 14:30:10 +0900
Subject: [PATCH v3 3/3] Skip index vacuuming when there is a risk of
wraparound.
If a table's relfrozenxid/relminmxid is too older than freeze max age
threshold (e.g., autovacuum_freeze_max_age * 1.5 XIDsold), we skip
index vacuuming to complete lazy vacuum quickly and advance
relfrozenxid/relminmxid.
---
src/backend/access/heap/vacuumlazy.c | 92 ++++++++++++++++++++++++----
src/backend/utils/misc/guc.c | 4 +-
src/include/postmaster/autovacuum.h | 6 ++
3 files changed, 89 insertions(+), 13 deletions(-)
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index 0bed78bd17..435a2df763 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -388,8 +388,9 @@ static void lazy_scan_heap(Relation onerel, VacuumParams *params,
static void two_pass_strategy(Relation onerel, LVRelStats *vacrelstats,
Relation *Irel, IndexBulkDeleteResult **indstats,
int nindexes, LVParallelState *lps,
- VacOptIndexCleanupValue index_cleanup,
- BlockNumber has_dead_items_pages, bool onecall);
+ VacuumParams *params, BlockNumber has_dead_items_pages,
+ bool onecall);
+static bool check_index_cleanup_xid_limit(Relation onerel);
static void lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats);
static bool lazy_check_needs_freeze(Buffer buf, bool *hastup,
LVRelStats *vacrelstats);
@@ -1619,8 +1620,7 @@ lazy_scan_heap(Relation onerel, VacuumParams *params, LVRelStats *vacrelstats,
/* Remove the collected garbage tuples from table and indexes */
two_pass_strategy(onerel, vacrelstats, Irel, indstats, nindexes,
- lps, params->index_cleanup,
- has_dead_items_pages, false);
+ lps, params, has_dead_items_pages, false);
/*
* Vacuum the Free Space Map to make newly-freed space visible on
@@ -1904,8 +1904,7 @@ lazy_scan_heap(Relation onerel, VacuumParams *params, LVRelStats *vacrelstats,
Assert(nindexes > 0 || dead_tuples->num_tuples == 0);
if (dead_tuples->num_tuples > 0)
two_pass_strategy(onerel, vacrelstats, Irel, indstats, nindexes,
- lps, params->index_cleanup,
- has_dead_items_pages, !calledtwopass);
+ lps, params, has_dead_items_pages, !calledtwopass);
/*
* Vacuum the remainder of the Free Space Map. We must do this whether or
@@ -1992,7 +1991,7 @@ lazy_scan_heap(Relation onerel, VacuumParams *params, LVRelStats *vacrelstats,
static void
two_pass_strategy(Relation onerel, LVRelStats *vacrelstats,
Relation *Irel, IndexBulkDeleteResult **indstats, int nindexes,
- LVParallelState *lps, VacOptIndexCleanupValue index_cleanup,
+ LVParallelState *lps, VacuumParams *params,
BlockNumber has_dead_items_pages, bool onecall)
{
bool skipping;
@@ -2018,17 +2017,30 @@ two_pass_strategy(Relation onerel, LVRelStats *vacrelstats,
* HOT-pruning but are not marked dead yet. We do not process them because
* it's a very rare condition, and the next vacuum will process them anyway.
*/
- if (index_cleanup == VACOPT_CLEANUP_DISABLED)
+ if (params->index_cleanup == VACOPT_CLEANUP_DISABLED)
skipping = true;
- else if (index_cleanup == VACOPT_CLEANUP_ENABLED)
+ else if (params->index_cleanup == VACOPT_CLEANUP_ENABLED)
skipping = false;
else if (!onecall)
skipping = false;
+
+ /*
+ * If a table is at risk of wraparound, we further check if the table's
+ * relfrozenxid/relminmxid is too older than freeze maximum age (e.g., more
+ * than autovacuum_freeze_max_age * 1.5 XIDs old). If so, we disable index
+ * vacuuming to quickly complete vacuum operation and advance relfrozenxid/relminmxid.
+ * Note that this can be applied to only autovacuum workers.
+ */
+ else if (params->is_wraparound && check_index_cleanup_xid_limit(onerel))
+ {
+ Assert(IsAutoVacuumWorkerProcess());
+ skipping = true;
+ }
else
{
BlockNumber rel_pages_threshold;
- Assert(onecall && index_cleanup == VACOPT_CLEANUP_AUTO);
+ Assert(onecall && params->index_cleanup == VACOPT_CLEANUP_AUTO);
rel_pages_threshold =
(double) vacrelstats->rel_pages * SKIP_VACUUM_PAGES_RATIO;
@@ -2062,7 +2074,7 @@ two_pass_strategy(Relation onerel, LVRelStats *vacrelstats,
* one or more LP_DEAD items (could be from us or from another
* VACUUM), not # blocks scanned.
*/
- if (index_cleanup == VACOPT_CLEANUP_AUTO)
+ if (params->index_cleanup == VACOPT_CLEANUP_AUTO)
ereport(elevel,
(errmsg("\"%s\": opted to not totally remove %d pruned items in %u pages",
vacrelstats->relname,
@@ -2084,6 +2096,64 @@ two_pass_strategy(Relation onerel, LVRelStats *vacrelstats,
vacrelstats->dead_tuples->num_tuples = 0;
}
+/*
+ * Return true if the table's relfrozenxid/relminmxid is too older than
+ * freeze age.
+ */
+static bool
+check_index_cleanup_xid_limit(Relation onerel)
+{
+ StdRdOptions *relopts = (StdRdOptions *) onerel->rd_options;
+ TransactionId xid_skip_limit;
+ MultiXactId multi_skip_limit;
+ int freeze_max_age;
+ int multixact_freeze_max_age;
+ int effective_multixact_freeze_max_age;
+
+ /*
+ * Check if table's relfrozenxid is too older than autovacuum_freeze_max_age
+ * (more than autovacuum_freeze_max_age * 1.5 XIDs old).
+ */
+ freeze_max_age = (relopts && relopts->autovacuum.freeze_max_age >= 0)
+ ? Min(relopts->autovacuum.freeze_max_age, autovacuum_freeze_max_age)
+ : autovacuum_freeze_max_age;
+ freeze_max_age = Min(freeze_max_age * 1.5, MAX_AUTOVACUUM_FREEZE_MAX_AGE);
+
+ xid_skip_limit = ReadNextTransactionId() - freeze_max_age;
+ if (!TransactionIdIsNormal(xid_skip_limit))
+ xid_skip_limit = FirstNormalTransactionId;
+
+ if (TransactionIdIsNormal(onerel->rd_rel->relfrozenxid) &&
+ TransactionIdPrecedes(onerel->rd_rel->relfrozenxid,
+ xid_skip_limit))
+ return true;
+
+ /*
+ * Similar to above, check multixact age. This is normally
+ * autovacuum_multixact_freeze_max_age, but may be less if we are short of
+ * multixact member space.
+ */
+ effective_multixact_freeze_max_age = MultiXactMemberFreezeThreshold();
+
+ multixact_freeze_max_age = (relopts && relopts->autovacuum.multixact_freeze_max_age >= 0)
+ ? Min(relopts->autovacuum.multixact_freeze_max_age,
+ effective_multixact_freeze_max_age)
+ : effective_multixact_freeze_max_age;
+ multixact_freeze_max_age = Min(multixact_freeze_max_age * 1.5,
+ MAX_AUTOVACUUM_FREEZE_MAX_AGE);
+
+ multi_skip_limit = ReadNextMultiXactId() - multixact_freeze_max_age;
+ if (multi_skip_limit < FirstMultiXactId)
+ multi_skip_limit = FirstMultiXactId;
+
+ if (MultiXactIdIsValid(onerel->rd_rel->relminmxid) &&
+ MultiXactIdPrecedes(onerel->rd_rel->relminmxid,
+ multi_skip_limit))
+ return true;
+
+ return false;
+}
+
/*
* lazy_vacuum_all_indexes() -- Main entry for index vacuuming
*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index b263e3493b..53aa444e13 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -3207,7 +3207,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
},
{
@@ -3217,7 +3217,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.
--
2.24.3 (Apple Git-128)