aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/heap/vacuumlazy.c2
-rw-r--r--src/backend/commands/vacuum.c46
-rw-r--r--src/backend/commands/vacuumparallel.c1
-rw-r--r--src/backend/postmaster/autovacuum.c291
-rw-r--r--src/include/commands/vacuum.h1
5 files changed, 219 insertions, 122 deletions
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index 2ba85bd3d6d..0a9ebd22bd0 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -389,7 +389,7 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
Assert(params->index_cleanup != VACOPTVALUE_UNSPECIFIED);
Assert(params->truncate != VACOPTVALUE_UNSPECIFIED &&
params->truncate != VACOPTVALUE_AUTO);
- VacuumFailsafeActive = false;
+ Assert(!VacuumFailsafeActive);
vacrel->consider_bypass_optimization = true;
vacrel->do_index_vacuuming = true;
vacrel->do_index_cleanup = true;
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index f2be74cdb5f..9386c08a556 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -48,6 +48,7 @@
#include "pgstat.h"
#include "postmaster/autovacuum.h"
#include "postmaster/bgworker_internals.h"
+#include "postmaster/interrupt.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/pmsignal.h"
@@ -523,9 +524,9 @@ vacuum(List *relations, VacuumParams *params, BufferAccessStrategy bstrategy,
{
ListCell *cur;
- VacuumUpdateCosts();
in_vacuum = true;
- VacuumCostActive = (vacuum_cost_delay > 0);
+ VacuumFailsafeActive = false;
+ VacuumUpdateCosts();
VacuumCostBalance = 0;
VacuumPageHit = 0;
VacuumPageMiss = 0;
@@ -579,12 +580,20 @@ vacuum(List *relations, VacuumParams *params, BufferAccessStrategy bstrategy,
CommandCounterIncrement();
}
}
+
+ /*
+ * Ensure VacuumFailsafeActive has been reset before vacuuming the
+ * next relation.
+ */
+ VacuumFailsafeActive = false;
}
}
PG_FINALLY();
{
in_vacuum = false;
VacuumCostActive = false;
+ VacuumFailsafeActive = false;
+ VacuumCostBalance = 0;
}
PG_END_TRY();
@@ -2245,7 +2254,28 @@ vacuum_delay_point(void)
/* Always check for interrupts */
CHECK_FOR_INTERRUPTS();
- if (!VacuumCostActive || InterruptPending)
+ if (InterruptPending ||
+ (!VacuumCostActive && !ConfigReloadPending))
+ return;
+
+ /*
+ * Autovacuum workers should reload the configuration file if requested.
+ * This allows changes to [autovacuum_]vacuum_cost_limit and
+ * [autovacuum_]vacuum_cost_delay to take effect while a table is being
+ * vacuumed or analyzed.
+ */
+ if (ConfigReloadPending && IsAutoVacuumWorkerProcess())
+ {
+ ConfigReloadPending = false;
+ ProcessConfigFile(PGC_SIGHUP);
+ VacuumUpdateCosts();
+ }
+
+ /*
+ * If we disabled cost-based delays after reloading the config file,
+ * return.
+ */
+ if (!VacuumCostActive)
return;
/*
@@ -2278,7 +2308,15 @@ vacuum_delay_point(void)
VacuumCostBalance = 0;
- VacuumUpdateCosts();
+ /*
+ * Balance and update limit values for autovacuum workers. We must do
+ * this periodically, as the number of workers across which we are
+ * balancing the limit may have changed.
+ *
+ * TODO: There may be better criteria for determining when to do this
+ * besides "check after napping".
+ */
+ AutoVacuumUpdateCostLimit();
/* Might have gotten an interrupt while sleeping */
CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/commands/vacuumparallel.c b/src/backend/commands/vacuumparallel.c
index cc0aff79044..e200d5caf82 100644
--- a/src/backend/commands/vacuumparallel.c
+++ b/src/backend/commands/vacuumparallel.c
@@ -995,7 +995,6 @@ parallel_vacuum_main(dsm_segment *seg, shm_toc *toc)
false);
/* Set cost-based vacuum delay */
- VacuumCostActive = (vacuum_cost_delay > 0);
VacuumUpdateCosts();
VacuumCostBalance = 0;
VacuumPageHit = 0;
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 0e7933c34d9..90cfc18f100 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -139,6 +139,18 @@ int Log_autovacuum_min_duration = 600000;
static bool am_autovacuum_launcher = false;
static bool am_autovacuum_worker = false;
+/*
+ * Variables to save the cost-related storage parameters for the current
+ * relation being vacuumed by this autovacuum worker. Using these, we can
+ * ensure we don't overwrite the values of vacuum_cost_delay and
+ * vacuum_cost_limit after reloading the configuration file. They are
+ * initialized to "invalid" values to indicate that no cost-related storage
+ * parameters were specified and will be set in do_autovacuum() after checking
+ * the storage parameters in table_recheck_autovac().
+ */
+static double av_storage_param_cost_delay = -1;
+static int av_storage_param_cost_limit = -1;
+
/* Flags set by signal handlers */
static volatile sig_atomic_t got_SIGUSR2 = false;
@@ -189,8 +201,8 @@ typedef struct autovac_table
{
Oid at_relid;
VacuumParams at_params;
- double at_vacuum_cost_delay;
- int at_vacuum_cost_limit;
+ double at_storage_param_vac_cost_delay;
+ int at_storage_param_vac_cost_limit;
bool at_dobalance;
bool at_sharedrel;
char *at_relname;
@@ -209,7 +221,7 @@ typedef struct autovac_table
* wi_sharedrel flag indicating whether table is marked relisshared
* wi_proc pointer to PGPROC of the running worker, NULL if not started
* wi_launchtime Time at which this worker was launched
- * wi_cost_* Vacuum cost-based delay parameters current in this worker
+ * wi_dobalance Whether this worker should be included in balance calculations
*
* All fields are protected by AutovacuumLock, except for wi_tableoid and
* wi_sharedrel which are protected by AutovacuumScheduleLock (note these
@@ -223,11 +235,8 @@ typedef struct WorkerInfoData
Oid wi_tableoid;
PGPROC *wi_proc;
TimestampTz wi_launchtime;
- bool wi_dobalance;
+ pg_atomic_flag wi_dobalance;
bool wi_sharedrel;
- double wi_cost_delay;
- int wi_cost_limit;
- int wi_cost_limit_base;
} WorkerInfoData;
typedef struct WorkerInfoData *WorkerInfo;
@@ -273,6 +282,8 @@ typedef struct AutoVacuumWorkItem
* av_startingWorker pointer to WorkerInfo currently being started (cleared by
* the worker itself as soon as it's up and running)
* av_workItems work item array
+ * av_nworkersForBalance the number of autovacuum workers to use when
+ * calculating the per worker cost limit
*
* This struct is protected by AutovacuumLock, except for av_signal and parts
* of the worker list (see above).
@@ -286,6 +297,7 @@ typedef struct
dlist_head av_runningWorkers;
WorkerInfo av_startingWorker;
AutoVacuumWorkItem av_workItems[NUM_WORKITEMS];
+ pg_atomic_uint32 av_nworkersForBalance;
} AutoVacuumShmemStruct;
static AutoVacuumShmemStruct *AutoVacuumShmem;
@@ -319,7 +331,7 @@ static void launch_worker(TimestampTz now);
static List *get_database_list(void);
static void rebuild_database_list(Oid newdb);
static int db_comparator(const void *a, const void *b);
-static void autovac_balance_cost(void);
+static void autovac_recalculate_workers_for_balance(void);
static void do_autovacuum(void);
static void FreeWorkerInfo(int code, Datum arg);
@@ -669,7 +681,7 @@ AutoVacLauncherMain(int argc, char *argv[])
{
LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
AutoVacuumShmem->av_signal[AutoVacRebalance] = false;
- autovac_balance_cost();
+ autovac_recalculate_workers_for_balance();
LWLockRelease(AutovacuumLock);
}
@@ -818,11 +830,6 @@ HandleAutoVacLauncherInterrupts(void)
if (!AutoVacuumingActive())
AutoVacLauncherShutdown();
- /* rebalance in case the default cost parameters changed */
- LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
- autovac_balance_cost();
- LWLockRelease(AutovacuumLock);
-
/* rebuild the list in case the naptime changed */
rebuild_database_list(InvalidOid);
}
@@ -1754,10 +1761,7 @@ FreeWorkerInfo(int code, Datum arg)
MyWorkerInfo->wi_sharedrel = false;
MyWorkerInfo->wi_proc = NULL;
MyWorkerInfo->wi_launchtime = 0;
- MyWorkerInfo->wi_dobalance = false;
- MyWorkerInfo->wi_cost_delay = 0;
- MyWorkerInfo->wi_cost_limit = 0;
- MyWorkerInfo->wi_cost_limit_base = 0;
+ pg_atomic_clear_flag(&MyWorkerInfo->wi_dobalance);
dlist_push_head(&AutoVacuumShmem->av_freeWorkers,
&MyWorkerInfo->wi_links);
/* not mine anymore */
@@ -1781,10 +1785,20 @@ FreeWorkerInfo(int code, Datum arg)
void
VacuumUpdateCosts(void)
{
+ double original_cost_delay = vacuum_cost_delay;
+ int original_cost_limit = vacuum_cost_limit;
+
if (MyWorkerInfo)
{
- vacuum_cost_delay = MyWorkerInfo->wi_cost_delay;
- vacuum_cost_limit = MyWorkerInfo->wi_cost_limit;
+ if (av_storage_param_cost_delay >= 0)
+ vacuum_cost_delay = av_storage_param_cost_delay;
+ else if (autovacuum_vac_cost_delay >= 0)
+ vacuum_cost_delay = autovacuum_vac_cost_delay;
+ else
+ /* fall back to VacuumCostDelay */
+ vacuum_cost_delay = VacuumCostDelay;
+
+ AutoVacuumUpdateCostLimit();
}
else
{
@@ -1792,88 +1806,128 @@ VacuumUpdateCosts(void)
vacuum_cost_delay = VacuumCostDelay;
vacuum_cost_limit = VacuumCostLimit;
}
+
+ /*
+ * If configuration changes are allowed to impact VacuumCostActive, make
+ * sure it is updated.
+ */
+ if (VacuumFailsafeActive)
+ Assert(!VacuumCostActive);
+ else if (vacuum_cost_delay > 0)
+ VacuumCostActive = true;
+ else
+ {
+ VacuumCostActive = false;
+ VacuumCostBalance = 0;
+ }
+
+ if (MyWorkerInfo)
+ {
+ Oid dboid,
+ tableoid;
+
+ /* Only log updates to cost-related variables */
+ if (vacuum_cost_delay == original_cost_delay &&
+ vacuum_cost_limit == original_cost_limit)
+ return;
+
+ Assert(!LWLockHeldByMe(AutovacuumLock));
+
+ LWLockAcquire(AutovacuumLock, LW_SHARED);
+ dboid = MyWorkerInfo->wi_dboid;
+ tableoid = MyWorkerInfo->wi_tableoid;
+ LWLockRelease(AutovacuumLock);
+
+ elog(DEBUG2,
+ "Autovacuum VacuumUpdateCosts(db=%u, rel=%u, dobalance=%s, cost_limit=%d, cost_delay=%g active=%s failsafe=%s)",
+ dboid, tableoid, pg_atomic_unlocked_test_flag(&MyWorkerInfo->wi_dobalance) ? "no" : "yes",
+ vacuum_cost_limit, vacuum_cost_delay,
+ vacuum_cost_delay > 0 ? "yes" : "no",
+ VacuumFailsafeActive ? "yes" : "no");
+
+ }
}
/*
- * autovac_balance_cost
- * Recalculate the cost limit setting for each active worker.
- *
- * Caller must hold the AutovacuumLock in exclusive mode.
+ * Update vacuum_cost_limit with the correct value for an autovacuum worker,
+ * given the value of other relevant cost limit parameters and the number of
+ * workers across which the limit must be balanced. Autovacuum workers must
+ * call this regularly in case av_nworkersForBalance has been updated by
+ * another worker or by the autovacuum launcher. They must also call it after a
+ * config reload.
*/
-static void
-autovac_balance_cost(void)
+void
+AutoVacuumUpdateCostLimit(void)
{
+ if (!MyWorkerInfo)
+ return;
+
/*
- * The idea here is that we ration out I/O equally. The amount of I/O
- * that a worker can consume is determined by cost_limit/cost_delay, so we
- * try to equalize those ratios rather than the raw limit settings.
- *
* note: in cost_limit, zero also means use value from elsewhere, because
* zero is not a valid value.
*/
- int vac_cost_limit = (autovacuum_vac_cost_limit > 0 ?
- autovacuum_vac_cost_limit : VacuumCostLimit);
- double vac_cost_delay = (autovacuum_vac_cost_delay >= 0 ?
- autovacuum_vac_cost_delay : VacuumCostDelay);
- double cost_total;
- double cost_avail;
- dlist_iter iter;
- /* not set? nothing to do */
- if (vac_cost_limit <= 0 || vac_cost_delay <= 0)
- return;
-
- /* calculate the total base cost limit of participating active workers */
- cost_total = 0.0;
- dlist_foreach(iter, &AutoVacuumShmem->av_runningWorkers)
+ if (av_storage_param_cost_limit > 0)
+ vacuum_cost_limit = av_storage_param_cost_limit;
+ else
{
- WorkerInfo worker = dlist_container(WorkerInfoData, wi_links, iter.cur);
+ int nworkers_for_balance;
+
+ if (autovacuum_vac_cost_limit > 0)
+ vacuum_cost_limit = autovacuum_vac_cost_limit;
+ else
+ vacuum_cost_limit = VacuumCostLimit;
+
+ /* Only balance limit if no cost-related storage parameters specified */
+ if (pg_atomic_unlocked_test_flag(&MyWorkerInfo->wi_dobalance))
+ return;
- if (worker->wi_proc != NULL &&
- worker->wi_dobalance &&
- worker->wi_cost_limit_base > 0 && worker->wi_cost_delay > 0)
- cost_total +=
- (double) worker->wi_cost_limit_base / worker->wi_cost_delay;
+ Assert(vacuum_cost_limit > 0);
+
+ nworkers_for_balance = pg_atomic_read_u32(&AutoVacuumShmem->av_nworkersForBalance);
+
+ /* There is at least 1 autovac worker (this worker) */
+ if (nworkers_for_balance <= 0)
+ elog(ERROR, "nworkers_for_balance must be > 0");
+
+ vacuum_cost_limit = Max(vacuum_cost_limit / nworkers_for_balance, 1);
}
+}
- /* there are no cost limits -- nothing to do */
- if (cost_total <= 0)
- return;
+/*
+ * autovac_recalculate_workers_for_balance
+ * Recalculate the number of workers to consider, given cost-related
+ * storage parameters and the current number of active workers.
+ *
+ * Caller must hold the AutovacuumLock in at least shared mode to access
+ * worker->wi_proc.
+ */
+static void
+autovac_recalculate_workers_for_balance(void)
+{
+ dlist_iter iter;
+ int orig_nworkers_for_balance;
+ int nworkers_for_balance = 0;
+
+ Assert(LWLockHeldByMe(AutovacuumLock));
+
+ orig_nworkers_for_balance =
+ pg_atomic_read_u32(&AutoVacuumShmem->av_nworkersForBalance);
- /*
- * Adjust cost limit of each active worker to balance the total of cost
- * limit to autovacuum_vacuum_cost_limit.
- */
- cost_avail = (double) vac_cost_limit / vac_cost_delay;
dlist_foreach(iter, &AutoVacuumShmem->av_runningWorkers)
{
WorkerInfo worker = dlist_container(WorkerInfoData, wi_links, iter.cur);
- if (worker->wi_proc != NULL &&
- worker->wi_dobalance &&
- worker->wi_cost_limit_base > 0 && worker->wi_cost_delay > 0)
- {
- int limit = (int)
- (cost_avail * worker->wi_cost_limit_base / cost_total);
-
- /*
- * We put a lower bound of 1 on the cost_limit, to avoid division-
- * by-zero in the vacuum code. Also, in case of roundoff trouble
- * in these calculations, let's be sure we don't ever set
- * cost_limit to more than the base value.
- */
- worker->wi_cost_limit = Max(Min(limit,
- worker->wi_cost_limit_base),
- 1);
- }
+ if (worker->wi_proc == NULL ||
+ pg_atomic_unlocked_test_flag(&worker->wi_dobalance))
+ continue;
- if (worker->wi_proc != NULL)
- elog(DEBUG2, "autovac_balance_cost(pid=%d db=%u, rel=%u, dobalance=%s cost_limit=%d, cost_limit_base=%d, cost_delay=%g)",
- worker->wi_proc->pid, worker->wi_dboid, worker->wi_tableoid,
- worker->wi_dobalance ? "yes" : "no",
- worker->wi_cost_limit, worker->wi_cost_limit_base,
- worker->wi_cost_delay);
+ nworkers_for_balance++;
}
+
+ if (nworkers_for_balance != orig_nworkers_for_balance)
+ pg_atomic_write_u32(&AutoVacuumShmem->av_nworkersForBalance,
+ nworkers_for_balance);
}
/*
@@ -2421,23 +2475,34 @@ do_autovacuum(void)
continue;
}
- /* Must hold AutovacuumLock while mucking with cost balance info */
- LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
+ /*
+ * Save the cost-related storage parameter values in global variables
+ * for reference when updating vacuum_cost_delay and vacuum_cost_limit
+ * during vacuuming this table.
+ */
+ av_storage_param_cost_delay = tab->at_storage_param_vac_cost_delay;
+ av_storage_param_cost_limit = tab->at_storage_param_vac_cost_limit;
- /* advertise my cost delay parameters for the balancing algorithm */
- MyWorkerInfo->wi_dobalance = tab->at_dobalance;
- MyWorkerInfo->wi_cost_delay = tab->at_vacuum_cost_delay;
- MyWorkerInfo->wi_cost_limit = tab->at_vacuum_cost_limit;
- MyWorkerInfo->wi_cost_limit_base = tab->at_vacuum_cost_limit;
+ /*
+ * We only expect this worker to ever set the flag, so don't bother
+ * checking the return value. We shouldn't have to retry.
+ */
+ if (tab->at_dobalance)
+ pg_atomic_test_set_flag(&MyWorkerInfo->wi_dobalance);
+ else
+ pg_atomic_clear_flag(&MyWorkerInfo->wi_dobalance);
- /* do a balance */
- autovac_balance_cost();
+ LWLockAcquire(AutovacuumLock, LW_SHARED);
+ autovac_recalculate_workers_for_balance();
+ LWLockRelease(AutovacuumLock);
- /* set the active cost parameters from the result of that */
+ /*
+ * We wait until this point to update cost delay and cost limit
+ * values, even though we reloaded the configuration file above, so
+ * that we can take into account the cost-related storage parameters.
+ */
VacuumUpdateCosts();
- /* done */
- LWLockRelease(AutovacuumLock);
/* clean up memory before each iteration */
MemoryContextResetAndDeleteChildren(PortalContext);
@@ -2521,16 +2586,17 @@ deleted:
pfree(tab);
/*
- * Remove my info from shared memory. We could, but intentionally
- * don't, clear wi_cost_limit and friends --- this is on the
- * assumption that we probably have more to do with similar cost
- * settings, so we don't want to give up our share of I/O for a very
- * short interval and thereby thrash the global balance.
+ * Remove my info from shared memory. We set wi_dobalance on the
+ * assumption that we are more likely than not to vacuum a table with
+ * no cost-related storage parameters next, so we want to claim our
+ * share of I/O as soon as possible to avoid thrashing the global
+ * balance.
*/
LWLockAcquire(AutovacuumScheduleLock, LW_EXCLUSIVE);
MyWorkerInfo->wi_tableoid = InvalidOid;
MyWorkerInfo->wi_sharedrel = false;
LWLockRelease(AutovacuumScheduleLock);
+ pg_atomic_test_set_flag(&MyWorkerInfo->wi_dobalance);
}
/*
@@ -2562,6 +2628,7 @@ deleted:
{
ConfigReloadPending = false;
ProcessConfigFile(PGC_SIGHUP);
+ VacuumUpdateCosts();
}
LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
@@ -2797,8 +2864,6 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
int freeze_table_age;
int multixact_freeze_min_age;
int multixact_freeze_table_age;
- int vac_cost_limit;
- double vac_cost_delay;
int log_min_duration;
/*
@@ -2808,20 +2873,6 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
* defaults, autovacuum's own first and plain vacuum second.
*/
- /* -1 in autovac setting means use plain vacuum_cost_delay */
- vac_cost_delay = (avopts && avopts->vacuum_cost_delay >= 0)
- ? avopts->vacuum_cost_delay
- : (autovacuum_vac_cost_delay >= 0)
- ? autovacuum_vac_cost_delay
- : VacuumCostDelay;
-
- /* 0 or -1 in autovac setting means use plain vacuum_cost_limit */
- vac_cost_limit = (avopts && avopts->vacuum_cost_limit > 0)
- ? avopts->vacuum_cost_limit
- : (autovacuum_vac_cost_limit > 0)
- ? autovacuum_vac_cost_limit
- : VacuumCostLimit;
-
/* -1 in autovac setting means use log_autovacuum_min_duration */
log_min_duration = (avopts && avopts->log_min_duration >= 0)
? avopts->log_min_duration
@@ -2877,8 +2928,10 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
tab->at_params.multixact_freeze_table_age = multixact_freeze_table_age;
tab->at_params.is_wraparound = wraparound;
tab->at_params.log_min_duration = log_min_duration;
- tab->at_vacuum_cost_limit = vac_cost_limit;
- tab->at_vacuum_cost_delay = vac_cost_delay;
+ tab->at_storage_param_vac_cost_limit = avopts ?
+ avopts->vacuum_cost_limit : 0;
+ tab->at_storage_param_vac_cost_delay = avopts ?
+ avopts->vacuum_cost_delay : -1;
tab->at_relname = NULL;
tab->at_nspname = NULL;
tab->at_datname = NULL;
@@ -3382,8 +3435,14 @@ AutoVacuumShmemInit(void)
/* initialize the WorkerInfo free list */
for (i = 0; i < autovacuum_max_workers; i++)
+ {
dlist_push_head(&AutoVacuumShmem->av_freeWorkers,
&worker[i].wi_links);
+ pg_atomic_init_flag(&worker[i].wi_dobalance);
+ }
+
+ pg_atomic_init_u32(&AutoVacuumShmem->av_nworkersForBalance, 0);
+
}
else
Assert(found);
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index 50caf1315dd..2a856b0e5ed 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -350,6 +350,7 @@ extern IndexBulkDeleteResult *vac_cleanup_one_index(IndexVacuumInfo *ivinfo,
extern Size vac_max_items_to_alloc_size(int max_items);
/* In postmaster/autovacuum.c */
+extern void AutoVacuumUpdateCostLimit(void);
extern void VacuumUpdateCosts(void);
/* in commands/vacuumparallel.c */