mirror of
https://github.com/postgres/postgres.git
synced 2025-11-19 13:42:17 +03:00
Allow changing autovacuum_max_workers without restarting.
This commit introduces a new parameter named autovacuum_worker_slots that controls how many autovacuum worker slots to reserve during server startup. Modifying this new parameter's value does require a server restart, but it should typically be set to the upper bound of what you might realistically need to set autovacuum_max_workers. With that new parameter in place, autovacuum_max_workers can now be changed with a SIGHUP (e.g., pg_ctl reload). If autovacuum_max_workers is set higher than autovacuum_worker_slots, a WARNING is emitted, and the server will only start up to autovacuum_worker_slots workers at a given time. If autovacuum_max_workers is set to a value less than the number of currently-running autovacuum workers, the existing workers will continue running, but no new workers will be started until the number of running autovacuum workers drops below autovacuum_max_workers. Reviewed-by: Sami Imseih, Justin Pryzby, Robert Haas, Andres Freund, Yogesh Sharma Discussion: https://postgr.es/m/20240410212344.GA1824549%40nathanxps13
This commit is contained in:
@@ -115,6 +115,7 @@
|
||||
* GUC parameters
|
||||
*/
|
||||
bool autovacuum_start_daemon = false;
|
||||
int autovacuum_worker_slots;
|
||||
int autovacuum_max_workers;
|
||||
int autovacuum_work_mem = -1;
|
||||
int autovacuum_naptime;
|
||||
@@ -210,7 +211,7 @@ typedef struct autovac_table
|
||||
/*-------------
|
||||
* This struct holds information about a single worker's whereabouts. We keep
|
||||
* an array of these in shared memory, sized according to
|
||||
* autovacuum_max_workers.
|
||||
* autovacuum_worker_slots.
|
||||
*
|
||||
* wi_links entry into free list or running list
|
||||
* wi_dboid OID of the database this worker is supposed to work on
|
||||
@@ -291,7 +292,7 @@ typedef struct
|
||||
{
|
||||
sig_atomic_t av_signal[AutoVacNumSignals];
|
||||
pid_t av_launcherpid;
|
||||
dlist_head av_freeWorkers;
|
||||
dclist_head av_freeWorkers;
|
||||
dlist_head av_runningWorkers;
|
||||
WorkerInfo av_startingWorker;
|
||||
AutoVacuumWorkItem av_workItems[NUM_WORKITEMS];
|
||||
@@ -349,6 +350,8 @@ static void autovac_report_activity(autovac_table *tab);
|
||||
static void autovac_report_workitem(AutoVacuumWorkItem *workitem,
|
||||
const char *nspname, const char *relname);
|
||||
static void avl_sigusr2_handler(SIGNAL_ARGS);
|
||||
static bool av_worker_available(void);
|
||||
static void check_av_worker_gucs(void);
|
||||
|
||||
|
||||
|
||||
@@ -577,8 +580,7 @@ AutoVacLauncherMain(char *startup_data, size_t startup_data_len)
|
||||
* wakening conditions.
|
||||
*/
|
||||
|
||||
launcher_determine_sleep(!dlist_is_empty(&AutoVacuumShmem->av_freeWorkers),
|
||||
false, &nap);
|
||||
launcher_determine_sleep(av_worker_available(), false, &nap);
|
||||
|
||||
/*
|
||||
* Wait until naptime expires or we get some type of signal (all the
|
||||
@@ -638,7 +640,7 @@ AutoVacLauncherMain(char *startup_data, size_t startup_data_len)
|
||||
current_time = GetCurrentTimestamp();
|
||||
LWLockAcquire(AutovacuumLock, LW_SHARED);
|
||||
|
||||
can_launch = !dlist_is_empty(&AutoVacuumShmem->av_freeWorkers);
|
||||
can_launch = av_worker_available();
|
||||
|
||||
if (AutoVacuumShmem->av_startingWorker != NULL)
|
||||
{
|
||||
@@ -681,8 +683,8 @@ AutoVacLauncherMain(char *startup_data, size_t startup_data_len)
|
||||
worker->wi_sharedrel = false;
|
||||
worker->wi_proc = NULL;
|
||||
worker->wi_launchtime = 0;
|
||||
dlist_push_head(&AutoVacuumShmem->av_freeWorkers,
|
||||
&worker->wi_links);
|
||||
dclist_push_head(&AutoVacuumShmem->av_freeWorkers,
|
||||
&worker->wi_links);
|
||||
AutoVacuumShmem->av_startingWorker = NULL;
|
||||
ereport(WARNING,
|
||||
errmsg("autovacuum worker took too long to start; canceled"));
|
||||
@@ -747,6 +749,8 @@ HandleAutoVacLauncherInterrupts(void)
|
||||
|
||||
if (ConfigReloadPending)
|
||||
{
|
||||
int autovacuum_max_workers_prev = autovacuum_max_workers;
|
||||
|
||||
ConfigReloadPending = false;
|
||||
ProcessConfigFile(PGC_SIGHUP);
|
||||
|
||||
@@ -754,6 +758,14 @@ HandleAutoVacLauncherInterrupts(void)
|
||||
if (!AutoVacuumingActive())
|
||||
AutoVacLauncherShutdown();
|
||||
|
||||
/*
|
||||
* If autovacuum_max_workers changed, emit a WARNING if
|
||||
* autovacuum_worker_slots < autovacuum_max_workers. If it didn't
|
||||
* change, skip this to avoid too many repeated log messages.
|
||||
*/
|
||||
if (autovacuum_max_workers_prev != autovacuum_max_workers)
|
||||
check_av_worker_gucs();
|
||||
|
||||
/* rebuild the list in case the naptime changed */
|
||||
rebuild_database_list(InvalidOid);
|
||||
}
|
||||
@@ -1089,7 +1101,7 @@ do_start_worker(void)
|
||||
|
||||
/* return quickly when there are no free workers */
|
||||
LWLockAcquire(AutovacuumLock, LW_SHARED);
|
||||
if (dlist_is_empty(&AutoVacuumShmem->av_freeWorkers))
|
||||
if (!av_worker_available())
|
||||
{
|
||||
LWLockRelease(AutovacuumLock);
|
||||
return InvalidOid;
|
||||
@@ -1242,7 +1254,7 @@ do_start_worker(void)
|
||||
* Get a worker entry from the freelist. We checked above, so there
|
||||
* really should be a free slot.
|
||||
*/
|
||||
wptr = dlist_pop_head_node(&AutoVacuumShmem->av_freeWorkers);
|
||||
wptr = dclist_pop_head_node(&AutoVacuumShmem->av_freeWorkers);
|
||||
|
||||
worker = dlist_container(WorkerInfoData, wi_links, wptr);
|
||||
worker->wi_dboid = avdb->adw_datid;
|
||||
@@ -1615,8 +1627,8 @@ FreeWorkerInfo(int code, Datum arg)
|
||||
MyWorkerInfo->wi_proc = NULL;
|
||||
MyWorkerInfo->wi_launchtime = 0;
|
||||
pg_atomic_clear_flag(&MyWorkerInfo->wi_dobalance);
|
||||
dlist_push_head(&AutoVacuumShmem->av_freeWorkers,
|
||||
&MyWorkerInfo->wi_links);
|
||||
dclist_push_head(&AutoVacuumShmem->av_freeWorkers,
|
||||
&MyWorkerInfo->wi_links);
|
||||
/* not mine anymore */
|
||||
MyWorkerInfo = NULL;
|
||||
|
||||
@@ -3248,10 +3260,14 @@ AutoVacuumRequestWork(AutoVacuumWorkItemType type, Oid relationId,
|
||||
void
|
||||
autovac_init(void)
|
||||
{
|
||||
if (autovacuum_start_daemon && !pgstat_track_counts)
|
||||
if (!autovacuum_start_daemon)
|
||||
return;
|
||||
else if (!pgstat_track_counts)
|
||||
ereport(WARNING,
|
||||
(errmsg("autovacuum not started because of misconfiguration"),
|
||||
errhint("Enable the \"track_counts\" option.")));
|
||||
else
|
||||
check_av_worker_gucs();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3268,7 +3284,7 @@ AutoVacuumShmemSize(void)
|
||||
*/
|
||||
size = sizeof(AutoVacuumShmemStruct);
|
||||
size = MAXALIGN(size);
|
||||
size = add_size(size, mul_size(autovacuum_max_workers,
|
||||
size = add_size(size, mul_size(autovacuum_worker_slots,
|
||||
sizeof(WorkerInfoData)));
|
||||
return size;
|
||||
}
|
||||
@@ -3295,7 +3311,7 @@ AutoVacuumShmemInit(void)
|
||||
Assert(!found);
|
||||
|
||||
AutoVacuumShmem->av_launcherpid = 0;
|
||||
dlist_init(&AutoVacuumShmem->av_freeWorkers);
|
||||
dclist_init(&AutoVacuumShmem->av_freeWorkers);
|
||||
dlist_init(&AutoVacuumShmem->av_runningWorkers);
|
||||
AutoVacuumShmem->av_startingWorker = NULL;
|
||||
memset(AutoVacuumShmem->av_workItems, 0,
|
||||
@@ -3305,10 +3321,10 @@ AutoVacuumShmemInit(void)
|
||||
MAXALIGN(sizeof(AutoVacuumShmemStruct)));
|
||||
|
||||
/* initialize the WorkerInfo free list */
|
||||
for (i = 0; i < autovacuum_max_workers; i++)
|
||||
for (i = 0; i < autovacuum_worker_slots; i++)
|
||||
{
|
||||
dlist_push_head(&AutoVacuumShmem->av_freeWorkers,
|
||||
&worker[i].wi_links);
|
||||
dclist_push_head(&AutoVacuumShmem->av_freeWorkers,
|
||||
&worker[i].wi_links);
|
||||
pg_atomic_init_flag(&worker[i].wi_dobalance);
|
||||
}
|
||||
|
||||
@@ -3344,3 +3360,35 @@ check_autovacuum_work_mem(int *newval, void **extra, GucSource source)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether there is a free autovacuum worker slot available.
|
||||
*/
|
||||
static bool
|
||||
av_worker_available(void)
|
||||
{
|
||||
int free_slots;
|
||||
int reserved_slots;
|
||||
|
||||
free_slots = dclist_count(&AutoVacuumShmem->av_freeWorkers);
|
||||
|
||||
reserved_slots = autovacuum_worker_slots - autovacuum_max_workers;
|
||||
reserved_slots = Max(0, reserved_slots);
|
||||
|
||||
return free_slots > reserved_slots;
|
||||
}
|
||||
|
||||
/*
|
||||
* Emits a WARNING if autovacuum_worker_slots < autovacuum_max_workers.
|
||||
*/
|
||||
static void
|
||||
check_av_worker_gucs(void)
|
||||
{
|
||||
if (autovacuum_worker_slots < autovacuum_max_workers)
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("\"autovacuum_max_workers\" (%d) should be less than or equal to \"autovacuum_worker_slots\" (%d)",
|
||||
autovacuum_max_workers, autovacuum_worker_slots),
|
||||
errdetail("The server will only start up to \"autovacuum_worker_slots\" (%d) autovacuum workers at a given time.",
|
||||
autovacuum_worker_slots)));
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* child process is allocated a PMChild struct from a fixed pool of structs.
|
||||
* The size of the pool is determined by various settings that configure how
|
||||
* many worker processes and backend connections are allowed, i.e.
|
||||
* autovacuum_max_workers, max_worker_processes, max_wal_senders, and
|
||||
* autovacuum_worker_slots, max_worker_processes, max_wal_senders, and
|
||||
* max_connections.
|
||||
*
|
||||
* Dead-end backends are handled slightly differently. There is no limit
|
||||
@@ -99,7 +99,7 @@ InitPostmasterChildSlots(void)
|
||||
*/
|
||||
pmchild_pools[B_BACKEND].size = 2 * (MaxConnections + max_wal_senders);
|
||||
|
||||
pmchild_pools[B_AUTOVAC_WORKER].size = autovacuum_max_workers;
|
||||
pmchild_pools[B_AUTOVAC_WORKER].size = autovacuum_worker_slots;
|
||||
pmchild_pools[B_BG_WORKER].size = max_worker_processes;
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user