aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/common/reloptions.c10
-rw-r--r--src/backend/access/heap/heapam.c1
-rw-r--r--src/backend/access/heap/vacuumlazy.c43
-rw-r--r--src/backend/commands/vacuum.c25
-rw-r--r--src/backend/postmaster/autovacuum.c10
-rw-r--r--src/backend/utils/misc/guc_tables.c14
-rw-r--r--src/backend/utils/misc/postgresql.conf.sample1
-rw-r--r--src/include/commands/vacuum.h12
-rw-r--r--src/include/utils/rel.h1
9 files changed, 115 insertions, 2 deletions
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 14c23101adf..dfa802416fc 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -314,6 +314,14 @@ static relopt_int intRelOpts[] =
},
{
{
+ "autovacuum_freeze_strategy_threshold",
+ "Table size at which VACUUM freezes using eager strategy, in megabytes.",
+ RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
+ ShareUpdateExclusiveLock
+ }, -1, 0, MAX_KILOBYTES
+ },
+ {
+ {
"log_autovacuum_min_duration",
"Sets the minimum execution time above which autovacuum actions will be logged",
RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
@@ -1863,6 +1871,8 @@ default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_max_age)},
{"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT,
offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_table_age)},
+ {"autovacuum_freeze_strategy_threshold", RELOPT_TYPE_INT,
+ offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_strategy_threshold)},
{"log_autovacuum_min_duration", RELOPT_TYPE_INT,
offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, log_min_duration)},
{"toast_tuple_target", RELOPT_TYPE_INT,
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index e6024a980bb..fec041d6cdd 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -7057,6 +7057,7 @@ heap_freeze_tuple(HeapTupleHeader tuple,
cutoffs.OldestMxact = MultiXactCutoff;
cutoffs.FreezeLimit = FreezeLimit;
cutoffs.MultiXactCutoff = MultiXactCutoff;
+ cutoffs.freeze_strategy_threshold_pages = 0;
pagefrz.freeze_required = true;
pagefrz.FreezePageRelfrozenXid = FreezeLimit;
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index 8f14cf85f38..e3f86493b90 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -153,6 +153,8 @@ typedef struct LVRelState
bool aggressive;
/* Use visibility map to skip? (disabled by DISABLE_PAGE_SKIPPING) */
bool skipwithvm;
+ /* Eagerly freeze pages that are eligible to become all-frozen? */
+ bool eager_freeze_strategy;
/* Wraparound failsafe has been triggered? */
bool failsafe_active;
/* Consider index vacuuming bypass optimization? */
@@ -243,6 +245,7 @@ typedef struct LVSavedErrInfo
/* non-export function prototypes */
static void lazy_scan_heap(LVRelState *vacrel);
+static void lazy_scan_strategy(LVRelState *vacrel);
static BlockNumber lazy_scan_skip(LVRelState *vacrel, Buffer *vmbuffer,
BlockNumber next_block,
bool *next_unskippable_allvis,
@@ -472,6 +475,10 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
vacrel->skipwithvm = skipwithvm;
+ /*
+ * Now determine VACUUM's freezing strategy
+ */
+ lazy_scan_strategy(vacrel);
if (verbose)
{
if (vacrel->aggressive)
@@ -1268,6 +1275,38 @@ lazy_scan_heap(LVRelState *vacrel)
}
/*
+ * lazy_scan_strategy() -- Determine freezing strategy.
+ *
+ * Our lazy freezing strategy is useful when putting off the work of freezing
+ * totally avoids freezing that turns out to have been wasted effort later on.
+ * Our eager freezing strategy is useful with larger tables that experience
+ * continual growth, where freezing pages proactively is needed just to avoid
+ * falling behind on freezing (eagerness is also likely to be cheaper in the
+ * short/medium term for such tables, but the long term picture matters most).
+ */
+static void
+lazy_scan_strategy(LVRelState *vacrel)
+{
+ BlockNumber rel_pages = vacrel->rel_pages;
+
+ /*
+ * Decide freezing strategy.
+ *
+ * The eager freezing strategy is used whenever rel_pages exceeds a
+ * threshold controlled by the freeze_strategy_threshold GUC/reloption.
+ *
+ * Also freeze eagerly with an unlogged or temp table, where the total
+ * cost of freezing pages is mostly just the cycles needed to prepare a
+ * set of freeze plans. Executing the freeze plans adds very little cost.
+ * Dirtying extra pages isn't a concern, either; VACUUM will definitely
+ * set PD_ALL_VISIBLE on affected pages, regardless of freezing strategy.
+ */
+ vacrel->eager_freeze_strategy =
+ (rel_pages > vacrel->cutoffs.freeze_strategy_threshold_pages ||
+ !RelationIsPermanent(vacrel->rel));
+}
+
+/*
* lazy_scan_skip() -- set up range of skippable blocks using visibility map.
*
* lazy_scan_heap() calls here every time it needs to set up a new range of
@@ -1795,10 +1834,12 @@ retry:
* one XID/MXID from before FreezeLimit/MultiXactCutoff is present. Also
* freeze when pruning generated an FPI, if doing so means that we set the
* page all-frozen afterwards (might not happen until final heap pass).
+ * When ongoing VACUUM opted to use the eager freezing strategy we freeze
+ * any page that will thereby become all-frozen in the visibility map.
*/
if (pagefrz.freeze_required || tuples_frozen == 0 ||
(prunestate->all_visible && prunestate->all_frozen &&
- fpi_before != pgWalUsage.wal_fpi))
+ (fpi_before != pgWalUsage.wal_fpi || vacrel->eager_freeze_strategy)))
{
/*
* We're freezing the page. Our final NewRelfrozenXid doesn't need to
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 7b1a4b127eb..033a9db90ce 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -68,6 +68,7 @@ int vacuum_freeze_min_age;
int vacuum_freeze_table_age;
int vacuum_multixact_freeze_min_age;
int vacuum_multixact_freeze_table_age;
+int vacuum_freeze_strategy_threshold;
int vacuum_failsafe_age;
int vacuum_multixact_failsafe_age;
@@ -264,6 +265,7 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
params.freeze_table_age = 0;
params.multixact_freeze_min_age = 0;
params.multixact_freeze_table_age = 0;
+ params.freeze_strategy_threshold = 0;
}
else
{
@@ -271,6 +273,7 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
params.freeze_table_age = -1;
params.multixact_freeze_min_age = -1;
params.multixact_freeze_table_age = -1;
+ params.freeze_strategy_threshold = -1;
}
/* user-invoked vacuum is never "for wraparound" */
@@ -962,7 +965,9 @@ vacuum_get_cutoffs(Relation rel, const VacuumParams *params,
multixact_freeze_min_age,
freeze_table_age,
multixact_freeze_table_age,
- effective_multixact_freeze_max_age;
+ effective_multixact_freeze_max_age,
+ freeze_strategy_threshold;
+ uint64 threshold_strategy_pages;
TransactionId nextXID,
safeOldestXmin,
aggressiveXIDCutoff;
@@ -975,6 +980,7 @@ vacuum_get_cutoffs(Relation rel, const VacuumParams *params,
multixact_freeze_min_age = params->multixact_freeze_min_age;
freeze_table_age = params->freeze_table_age;
multixact_freeze_table_age = params->multixact_freeze_table_age;
+ freeze_strategy_threshold = params->freeze_strategy_threshold;
/* Set pg_class fields in cutoffs */
cutoffs->relfrozenxid = rel->rd_rel->relfrozenxid;
@@ -1090,6 +1096,23 @@ vacuum_get_cutoffs(Relation rel, const VacuumParams *params,
cutoffs->MultiXactCutoff = cutoffs->OldestMxact;
/*
+ * Determine the freeze_strategy_threshold to use: as specified by the
+ * caller, or vacuum_freeze_strategy_threshold
+ */
+ if (freeze_strategy_threshold < 0)
+ freeze_strategy_threshold = vacuum_freeze_strategy_threshold;
+ Assert(freeze_strategy_threshold >= 0);
+
+ /*
+ * Convert MB-based freeze_strategy_threshold to page-based value used by
+ * our vacuumlazy.c caller, while being careful to avoid overflow
+ */
+ threshold_strategy_pages =
+ ((uint64) freeze_strategy_threshold * 1024 * 1024) / BLCKSZ;
+ threshold_strategy_pages = Min(threshold_strategy_pages, MaxBlockNumber);
+ cutoffs->freeze_strategy_threshold_pages = threshold_strategy_pages;
+
+ /*
* Finally, figure out if caller needs to do an aggressive VACUUM or not.
*
* Determine the table freeze age to use: as specified by the caller, or
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index f5ea381c53e..ecddde3a1ea 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -151,6 +151,7 @@ static int default_freeze_min_age;
static int default_freeze_table_age;
static int default_multixact_freeze_min_age;
static int default_multixact_freeze_table_age;
+static int default_freeze_strategy_threshold;
/* Memory context for long-lived data */
static MemoryContext AutovacMemCxt;
@@ -2010,6 +2011,7 @@ do_autovacuum(void)
default_freeze_table_age = 0;
default_multixact_freeze_min_age = 0;
default_multixact_freeze_table_age = 0;
+ default_freeze_strategy_threshold = 0;
}
else
{
@@ -2017,6 +2019,7 @@ do_autovacuum(void)
default_freeze_table_age = vacuum_freeze_table_age;
default_multixact_freeze_min_age = vacuum_multixact_freeze_min_age;
default_multixact_freeze_table_age = vacuum_multixact_freeze_table_age;
+ default_freeze_strategy_threshold = vacuum_freeze_strategy_threshold;
}
ReleaseSysCache(tuple);
@@ -2801,6 +2804,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
int freeze_table_age;
int multixact_freeze_min_age;
int multixact_freeze_table_age;
+ int freeze_strategy_threshold;
int vac_cost_limit;
double vac_cost_delay;
int log_min_duration;
@@ -2850,6 +2854,11 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
? avopts->multixact_freeze_table_age
: default_multixact_freeze_table_age;
+ freeze_strategy_threshold = (avopts &&
+ avopts->freeze_strategy_threshold >= 0)
+ ? avopts->freeze_strategy_threshold
+ : default_freeze_strategy_threshold;
+
tab = palloc(sizeof(autovac_table));
tab->at_relid = relid;
tab->at_sharedrel = classForm->relisshared;
@@ -2877,6 +2886,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
tab->at_params.freeze_table_age = freeze_table_age;
tab->at_params.multixact_freeze_min_age = multixact_freeze_min_age;
tab->at_params.multixact_freeze_table_age = multixact_freeze_table_age;
+ tab->at_params.freeze_strategy_threshold = freeze_strategy_threshold;
tab->at_params.is_wraparound = wraparound;
tab->at_params.log_min_duration = log_min_duration;
tab->at_vacuum_cost_limit = vac_cost_limit;
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 4ac808ed224..dcfa0c4c4b3 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -2536,6 +2536,20 @@ struct config_int ConfigureNamesInt[] =
},
{
+ {"vacuum_freeze_strategy_threshold", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Table size at which VACUUM freezes using eager strategy, in megabytes."),
+ gettext_noop("This is applied by comparing it to the size of a table's main fork at "
+ "the beginning of each VACUUM. Eager freezing strategy is used when size "
+ "exceeds the threshold, or when table is a temporary or unlogged table. "
+ "Otherwise lazy freezing strategy is used."),
+ GUC_UNIT_MB
+ },
+ &vacuum_freeze_strategy_threshold,
+ 4096, 0, MAX_KILOBYTES,
+ NULL, NULL, NULL
+ },
+
+ {
{"vacuum_defer_cleanup_age", PGC_SIGHUP, REPLICATION_PRIMARY,
gettext_noop("Number of transactions by which VACUUM and HOT cleanup should be deferred, if any."),
NULL
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index d06074b86f6..fda695e7567 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -700,6 +700,7 @@
#vacuum_multixact_freeze_table_age = 150000000
#vacuum_multixact_freeze_min_age = 5000000
#vacuum_multixact_failsafe_age = 1600000000
+#vacuum_freeze_strategy_threshold = 4GB
#bytea_output = 'hex' # hex, escape
#xmlbinary = 'base64'
#xmloption = 'content'
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index 689dbb77024..2a4dae7dd87 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -222,6 +222,9 @@ typedef struct VacuumParams
* use default */
int multixact_freeze_table_age; /* multixact age at which to scan
* whole table */
+ int freeze_strategy_threshold; /* threshold to use eager
+ * freezing, in megabytes, -1 to
+ * use default */
bool is_wraparound; /* force a for-wraparound vacuum */
int log_min_duration; /* minimum execution threshold in ms at
* which autovacuum is logged, -1 to use
@@ -274,6 +277,14 @@ struct VacuumCutoffs
*/
TransactionId FreezeLimit;
MultiXactId MultiXactCutoff;
+
+ /*
+ * Eager freezing strategy is used whenever target rel's main fork size
+ * exceeds freeze_strategy_threshold_pages. Otherwise lazy freezing
+ * strategy is used. (Actually, there are exceptions. Non-permanent
+ * tables always use eager freezing strategy.)
+ */
+ BlockNumber freeze_strategy_threshold_pages;
};
/*
@@ -297,6 +308,7 @@ extern PGDLLIMPORT int vacuum_freeze_min_age;
extern PGDLLIMPORT int vacuum_freeze_table_age;
extern PGDLLIMPORT int vacuum_multixact_freeze_min_age;
extern PGDLLIMPORT int vacuum_multixact_freeze_table_age;
+extern PGDLLIMPORT int vacuum_freeze_strategy_threshold;
extern PGDLLIMPORT int vacuum_failsafe_age;
extern PGDLLIMPORT int vacuum_multixact_failsafe_age;
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index af9785038d3..39c7ccf0c89 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -314,6 +314,7 @@ typedef struct AutoVacOpts
int multixact_freeze_min_age;
int multixact_freeze_max_age;
int multixact_freeze_table_age;
+ int freeze_strategy_threshold;
int log_min_duration;
float8 vacuum_cost_delay;
float8 vacuum_scale_factor;