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>
</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>
}
}
- 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);
/* 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;
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);
{
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)
{
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;
}
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;
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;
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);
MaxLivePostmasterChildren(void)
{
return 2 * (MaxConnections + autovacuum_max_workers + 1 +
- GetNumRegisteredBackgroundWorkers(0));
+ max_worker_processes);
}
/*
{
RegisteredBgWorker *rw;
int namelen = strlen(worker->bgw_name);
- static int maxworkers;
static int numworkers = 0;
#ifdef EXEC_BACKEND
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)));
/*
* 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;
}
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)
* 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
*/
int NBuffers = 1000;
int MaxConnections = 90;
+int max_worker_processes = 8;
int MaxBackends = 0;
int VacuumCostPageHit = 1; /* GUC parameters for vacuum */
/* 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)
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);
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."),
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;
}
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;
}
# - Asynchronous Behavior -
#effective_io_concurrency = 1 # 1-1000; 0 disables prefetching
+#max_worker_processes = 8
#------------------------------------------------------------------------------
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"),
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;
*/
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;
/*
* 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
{
typedef struct xl_parameter_change
{
int MaxConnections;
+ int max_worker_processes;
int max_prepared_xacts;
int max_locks_per_xact;
int wal_level;
*/
int wal_level;
int MaxConnections;
+ int max_worker_processes;
int max_prepared_xacts;
int max_locks_per_xact;
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;