mirror of https://github.com/postgres/postgres
Add new GUC, max_worker_processes, limiting number of bgworkers.
In 9.3, there's no particular limit on the number of bgworkers; instead, we just count up the number that are actually registered, and use that to set MaxBackends. However, that approach causes problems for Hot Standby, which needs both MaxBackends and the size of the lock table to be the same on the standby as on the master, yet it may not be desirable to run the same bgworkers in both places. 9.3 handles that by failing to notice the problem, which will probably work fine in nearly all cases anyway, but is not theoretically sound. A further problem with simply counting the number of registered workers is that new workers can't be registered without a postmaster restart. This is inconvenient for administrators, since bouncing the postmaster causes an interruption of service. Moreover, there are a number of applications for background processes where, by necessity, the background process must be started on the fly (e.g. parallel query). While this patch doesn't actually make it possible to register new background workers after startup time, it's a necessary prerequisite. Patch by me. Review by Michael Paquier.
This commit is contained in:
parent
5cbe935c9d
commit
6bc8ef0b7f
|
@ -146,4 +146,9 @@ typedef struct BackgroundWorker
|
|||
The <filename>worker_spi</> contrib module contains a working example,
|
||||
which demonstrates some useful techniques.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The maximum number of registered background workers is limited by
|
||||
<xref linkend="guc-max-worker-processes">.
|
||||
</para>
|
||||
</chapter>
|
||||
|
|
|
@ -1595,6 +1595,25 @@ include 'filename'
|
|||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="guc-max-worker-processes" xreflabel="max_worrker_processes">
|
||||
<term><varname>max_worker_processes</varname> (<type>integer</type>)</term>
|
||||
<indexterm>
|
||||
<primary><varname>max_worker_processes</> configuration parameter</primary>
|
||||
</indexterm>
|
||||
<listitem>
|
||||
<para>
|
||||
Sets the maximum number of background processes that the system
|
||||
can support. This parameter can only be set at server start.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
When running a standby server, you must set this parameter to the
|
||||
same or higher value than on the master server. Otherwise, queries
|
||||
will not be allowed in the standby server.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
|
|
@ -117,8 +117,9 @@ xlog_desc(StringInfo buf, uint8 xl_info, char *rec)
|
|||
}
|
||||
}
|
||||
|
||||
appendStringInfo(buf, "parameter change: max_connections=%d max_prepared_xacts=%d max_locks_per_xact=%d wal_level=%s",
|
||||
appendStringInfo(buf, "parameter change: max_connections=%d max_worker_processes=%d max_prepared_xacts=%d max_locks_per_xact=%d wal_level=%s",
|
||||
xlrec.MaxConnections,
|
||||
xlrec.max_worker_processes,
|
||||
xlrec.max_prepared_xacts,
|
||||
xlrec.max_locks_per_xact,
|
||||
wal_level_str);
|
||||
|
|
|
@ -4134,6 +4134,7 @@ BootStrapXLOG(void)
|
|||
|
||||
/* Set important parameter values for use when replaying WAL */
|
||||
ControlFile->MaxConnections = MaxConnections;
|
||||
ControlFile->max_worker_processes = max_worker_processes;
|
||||
ControlFile->max_prepared_xacts = max_prepared_xacts;
|
||||
ControlFile->max_locks_per_xact = max_locks_per_xact;
|
||||
ControlFile->wal_level = wal_level;
|
||||
|
@ -4841,6 +4842,9 @@ CheckRequiredParameterValues(void)
|
|||
RecoveryRequiresIntParameter("max_connections",
|
||||
MaxConnections,
|
||||
ControlFile->MaxConnections);
|
||||
RecoveryRequiresIntParameter("max_worker_processes",
|
||||
max_worker_processes,
|
||||
ControlFile->max_worker_processes);
|
||||
RecoveryRequiresIntParameter("max_prepared_transactions",
|
||||
max_prepared_xacts,
|
||||
ControlFile->max_prepared_xacts);
|
||||
|
@ -7770,6 +7774,7 @@ XLogReportParameters(void)
|
|||
{
|
||||
if (wal_level != ControlFile->wal_level ||
|
||||
MaxConnections != ControlFile->MaxConnections ||
|
||||
max_worker_processes != ControlFile->max_worker_processes ||
|
||||
max_prepared_xacts != ControlFile->max_prepared_xacts ||
|
||||
max_locks_per_xact != ControlFile->max_locks_per_xact)
|
||||
{
|
||||
|
@ -7786,6 +7791,7 @@ XLogReportParameters(void)
|
|||
xl_parameter_change xlrec;
|
||||
|
||||
xlrec.MaxConnections = MaxConnections;
|
||||
xlrec.max_worker_processes = max_worker_processes;
|
||||
xlrec.max_prepared_xacts = max_prepared_xacts;
|
||||
xlrec.max_locks_per_xact = max_locks_per_xact;
|
||||
xlrec.wal_level = wal_level;
|
||||
|
@ -7799,6 +7805,7 @@ XLogReportParameters(void)
|
|||
}
|
||||
|
||||
ControlFile->MaxConnections = MaxConnections;
|
||||
ControlFile->max_worker_processes = max_worker_processes;
|
||||
ControlFile->max_prepared_xacts = max_prepared_xacts;
|
||||
ControlFile->max_locks_per_xact = max_locks_per_xact;
|
||||
ControlFile->wal_level = wal_level;
|
||||
|
@ -8184,6 +8191,7 @@ xlog_redo(XLogRecPtr lsn, XLogRecord *record)
|
|||
|
||||
LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);
|
||||
ControlFile->MaxConnections = xlrec.MaxConnections;
|
||||
ControlFile->max_worker_processes = xlrec.max_worker_processes;
|
||||
ControlFile->max_prepared_xacts = xlrec.max_prepared_xacts;
|
||||
ControlFile->max_locks_per_xact = xlrec.max_locks_per_xact;
|
||||
ControlFile->wal_level = xlrec.wal_level;
|
||||
|
|
|
@ -402,7 +402,6 @@ static void reaper(SIGNAL_ARGS);
|
|||
static void sigusr1_handler(SIGNAL_ARGS);
|
||||
static void startup_die(SIGNAL_ARGS);
|
||||
static void dummy_handler(SIGNAL_ARGS);
|
||||
static int GetNumRegisteredBackgroundWorkers(int flags);
|
||||
static void StartupPacketTimeoutHandler(void);
|
||||
static void CleanupBackend(int pid, int exitstatus);
|
||||
static bool CleanupBackgroundWorker(int pid, int exitstatus);
|
||||
|
@ -5212,7 +5211,7 @@ int
|
|||
MaxLivePostmasterChildren(void)
|
||||
{
|
||||
return 2 * (MaxConnections + autovacuum_max_workers + 1 +
|
||||
GetNumRegisteredBackgroundWorkers(0));
|
||||
max_worker_processes);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -5226,7 +5225,6 @@ RegisterBackgroundWorker(BackgroundWorker *worker)
|
|||
{
|
||||
RegisteredBgWorker *rw;
|
||||
int namelen = strlen(worker->bgw_name);
|
||||
static int maxworkers;
|
||||
static int numworkers = 0;
|
||||
|
||||
#ifdef EXEC_BACKEND
|
||||
|
@ -5238,11 +5236,6 @@ RegisterBackgroundWorker(BackgroundWorker *worker)
|
|||
static int BackgroundWorkerCookie = 1;
|
||||
#endif
|
||||
|
||||
/* initialize upper limit on first call */
|
||||
if (numworkers == 0)
|
||||
maxworkers = MAX_BACKENDS -
|
||||
(MaxConnections + autovacuum_max_workers + 1);
|
||||
|
||||
if (!IsUnderPostmaster)
|
||||
ereport(LOG,
|
||||
(errmsg("registering background worker: %s", worker->bgw_name)));
|
||||
|
@ -5298,17 +5291,17 @@ RegisterBackgroundWorker(BackgroundWorker *worker)
|
|||
/*
|
||||
* Enforce maximum number of workers. Note this is overly restrictive: we
|
||||
* could allow more non-shmem-connected workers, because these don't count
|
||||
* towards the MAX_BACKENDS limit elsewhere. This doesn't really matter
|
||||
* for practical purposes; several million processes would need to run on
|
||||
* a single server.
|
||||
* towards the MAX_BACKENDS limit elsewhere. For now, it doesn't seem
|
||||
* important to relax this restriction.
|
||||
*/
|
||||
if (++numworkers > maxworkers)
|
||||
if (++numworkers > max_worker_processes)
|
||||
{
|
||||
ereport(LOG,
|
||||
(errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
|
||||
errmsg("too many background workers"),
|
||||
errdetail("Up to %d background workers can be registered with the current settings.",
|
||||
maxworkers)));
|
||||
max_worker_processes),
|
||||
errhint("Consider increasing the configuration parameter \"max_worker_processes\".")));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -5589,41 +5582,6 @@ do_start_bgworker(void)
|
|||
proc_exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the number of background workers registered that have at least
|
||||
* one of the passed flag bits set.
|
||||
*/
|
||||
static int
|
||||
GetNumRegisteredBackgroundWorkers(int flags)
|
||||
{
|
||||
slist_iter iter;
|
||||
int count = 0;
|
||||
|
||||
slist_foreach(iter, &BackgroundWorkerList)
|
||||
{
|
||||
RegisteredBgWorker *rw;
|
||||
|
||||
rw = slist_container(RegisteredBgWorker, rw_lnode, iter.cur);
|
||||
|
||||
if (flags != 0 &&
|
||||
!(rw->rw_worker.bgw_flags & flags))
|
||||
continue;
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the number of bgworkers that need to have PGPROC entries.
|
||||
*/
|
||||
int
|
||||
GetNumShmemAttachedBgworkers(void)
|
||||
{
|
||||
return GetNumRegisteredBackgroundWorkers(BGWORKER_SHMEM_ACCESS);
|
||||
}
|
||||
|
||||
#ifdef EXEC_BACKEND
|
||||
static pid_t
|
||||
bgworker_forkexec(int cookie)
|
||||
|
|
|
@ -140,10 +140,8 @@ ProcGlobalSemas(void)
|
|||
* running out when trying to start another backend is a common failure.
|
||||
* So, now we grab enough semaphores to support the desired max number
|
||||
* of backends immediately at initialization --- if the sysadmin has set
|
||||
* MaxConnections or autovacuum_max_workers higher than his kernel will
|
||||
* support, he'll find out sooner rather than later. (The number of
|
||||
* background worker processes registered by loadable modules is also taken
|
||||
* into consideration.)
|
||||
* MaxConnections, max_worker_processes, or autovacuum_max_workers higher
|
||||
* than his kernel will support, he'll find out sooner rather than later.
|
||||
*
|
||||
* Another reason for creating semaphores here is that the semaphore
|
||||
* implementation typically requires us to create semaphores in the
|
||||
|
|
|
@ -109,6 +109,7 @@ int maintenance_work_mem = 16384;
|
|||
*/
|
||||
int NBuffers = 1000;
|
||||
int MaxConnections = 90;
|
||||
int max_worker_processes = 8;
|
||||
int MaxBackends = 0;
|
||||
|
||||
int VacuumCostPageHit = 1; /* GUC parameters for vacuum */
|
||||
|
|
|
@ -436,7 +436,7 @@ InitializeMaxBackends(void)
|
|||
|
||||
/* the extra unit accounts for the autovacuum launcher */
|
||||
MaxBackends = MaxConnections + autovacuum_max_workers + 1 +
|
||||
GetNumShmemAttachedBgworkers();
|
||||
+ max_worker_processes;
|
||||
|
||||
/* internal error because the values were all checked previously */
|
||||
if (MaxBackends > MAX_BACKENDS)
|
||||
|
|
|
@ -190,6 +190,7 @@ static const char *show_tcp_keepalives_idle(void);
|
|||
static const char *show_tcp_keepalives_interval(void);
|
||||
static const char *show_tcp_keepalives_count(void);
|
||||
static bool check_maxconnections(int *newval, void **extra, GucSource source);
|
||||
static bool check_max_worker_processes(int *newval, void **extra, GucSource source);
|
||||
static bool check_autovacuum_max_workers(int *newval, void **extra, GucSource source);
|
||||
static bool check_effective_io_concurrency(int *newval, void **extra, GucSource source);
|
||||
static void assign_effective_io_concurrency(int newval, void *extra);
|
||||
|
@ -2158,6 +2159,18 @@ static struct config_int ConfigureNamesInt[] =
|
|||
check_effective_io_concurrency, assign_effective_io_concurrency, NULL
|
||||
},
|
||||
|
||||
{
|
||||
{"max_worker_processes",
|
||||
PGC_POSTMASTER,
|
||||
RESOURCES_ASYNCHRONOUS,
|
||||
gettext_noop("Maximum number of concurrent worker processes."),
|
||||
NULL,
|
||||
},
|
||||
&max_worker_processes,
|
||||
8, 1, MAX_BACKENDS,
|
||||
check_max_worker_processes, NULL, NULL
|
||||
},
|
||||
|
||||
{
|
||||
{"log_rotation_age", PGC_SIGHUP, LOGGING_WHERE,
|
||||
gettext_noop("Automatic log file rotation will occur after N minutes."),
|
||||
|
@ -8667,8 +8680,8 @@ show_tcp_keepalives_count(void)
|
|||
static bool
|
||||
check_maxconnections(int *newval, void **extra, GucSource source)
|
||||
{
|
||||
if (*newval + GetNumShmemAttachedBgworkers() + autovacuum_max_workers + 1 >
|
||||
MAX_BACKENDS)
|
||||
if (*newval + autovacuum_max_workers + 1 +
|
||||
max_worker_processes > MAX_BACKENDS)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
@ -8676,8 +8689,15 @@ check_maxconnections(int *newval, void **extra, GucSource source)
|
|||
static bool
|
||||
check_autovacuum_max_workers(int *newval, void **extra, GucSource source)
|
||||
{
|
||||
if (MaxConnections + *newval + 1 + GetNumShmemAttachedBgworkers() >
|
||||
MAX_BACKENDS)
|
||||
if (MaxConnections + *newval + 1 + max_worker_processes > MAX_BACKENDS)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
check_max_worker_processes(int *newval, void **extra, GucSource source)
|
||||
{
|
||||
if (MaxConnections + autovacuum_max_workers + 1 + *newval > MAX_BACKENDS)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -152,6 +152,7 @@
|
|||
# - Asynchronous Behavior -
|
||||
|
||||
#effective_io_concurrency = 1 # 1-1000; 0 disables prefetching
|
||||
#max_worker_processes = 8
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
|
|
@ -260,6 +260,8 @@ main(int argc, char *argv[])
|
|||
wal_level_str(ControlFile.wal_level));
|
||||
printf(_("Current max_connections setting: %d\n"),
|
||||
ControlFile.MaxConnections);
|
||||
printf(_("Current max_worker_processes setting: %d\n"),
|
||||
ControlFile.max_worker_processes);
|
||||
printf(_("Current max_prepared_xacts setting: %d\n"),
|
||||
ControlFile.max_prepared_xacts);
|
||||
printf(_("Current max_locks_per_xact setting: %d\n"),
|
||||
|
|
|
@ -518,6 +518,7 @@ GuessControlValues(void)
|
|||
|
||||
ControlFile.wal_level = WAL_LEVEL_MINIMAL;
|
||||
ControlFile.MaxConnections = 100;
|
||||
ControlFile.max_worker_processes = 8;
|
||||
ControlFile.max_prepared_xacts = 0;
|
||||
ControlFile.max_locks_per_xact = 64;
|
||||
|
||||
|
@ -664,6 +665,7 @@ RewriteControlFile(void)
|
|||
*/
|
||||
ControlFile.wal_level = WAL_LEVEL_MINIMAL;
|
||||
ControlFile.MaxConnections = 100;
|
||||
ControlFile.max_worker_processes = 8;
|
||||
ControlFile.max_prepared_xacts = 0;
|
||||
ControlFile.max_locks_per_xact = 64;
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ typedef struct BkpBlock
|
|||
/*
|
||||
* Each page of XLOG file has a header like this:
|
||||
*/
|
||||
#define XLOG_PAGE_MAGIC 0xD075 /* can be used as WAL version indicator */
|
||||
#define XLOG_PAGE_MAGIC 0xD076 /* can be used as WAL version indicator */
|
||||
|
||||
typedef struct XLogPageHeaderData
|
||||
{
|
||||
|
@ -205,6 +205,7 @@ typedef XLogLongPageHeaderData *XLogLongPageHeader;
|
|||
typedef struct xl_parameter_change
|
||||
{
|
||||
int MaxConnections;
|
||||
int max_worker_processes;
|
||||
int max_prepared_xacts;
|
||||
int max_locks_per_xact;
|
||||
int wal_level;
|
||||
|
|
|
@ -172,6 +172,7 @@ typedef struct ControlFileData
|
|||
*/
|
||||
int wal_level;
|
||||
int MaxConnections;
|
||||
int max_worker_processes;
|
||||
int max_prepared_xacts;
|
||||
int max_locks_per_xact;
|
||||
|
||||
|
|
|
@ -141,6 +141,7 @@ extern PGDLLIMPORT char *DataDir;
|
|||
extern PGDLLIMPORT int NBuffers;
|
||||
extern int MaxBackends;
|
||||
extern int MaxConnections;
|
||||
extern int max_worker_processes;
|
||||
|
||||
extern PGDLLIMPORT int MyProcPid;
|
||||
extern PGDLLIMPORT pg_time_t MyStartTime;
|
||||
|
|
Loading…
Reference in New Issue