diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 7e208a4b81..07b847a8e9 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -697,8 +697,7 @@ include_dir 'conf.d' The default value is three connections. The value must be less - than max_connections minus - . + than max_connections. This parameter can only be set at server start. @@ -3495,24 +3494,25 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows - Specifies the maximum number of concurrent connections from - standby servers or streaming base backup clients (i.e., the - maximum number of simultaneously running WAL sender - processes). The default is 10. The value 0 means replication is - disabled. WAL sender processes count towards the total number - of connections, so this parameter's value must be less than - minus - . - Abrupt streaming client disconnection might leave an orphaned - connection slot behind until - a timeout is reached, so this parameter should be set slightly - higher than the maximum number of expected clients so disconnected - clients can immediately reconnect. This parameter can only - be set at server start. - Also, wal_level must be set to + Specifies the maximum number of concurrent connections from standby + servers or streaming base backup clients (i.e., the maximum number of + simultaneously running WAL sender processes). The default is + 10. The value 0 means + replication is disabled. Abrupt streaming client disconnection might + leave an orphaned connection slot behind until a timeout is reached, + so this parameter should be set slightly higher than the maximum + number of expected clients so disconnected clients can immediately + reconnect. This parameter can only be set at server start. Also, + wal_level must be set to replica or higher to allow connections from standby servers. + + + 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. + diff --git a/doc/src/sgml/high-availability.sgml b/doc/src/sgml/high-availability.sgml index bbab7395a2..2b4dcd03c8 100644 --- a/doc/src/sgml/high-availability.sgml +++ b/doc/src/sgml/high-availability.sgml @@ -2177,6 +2177,11 @@ LOG: database system is ready to accept read only connections max_locks_per_transaction + + + max_wal_senders + + max_worker_processes diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml index 1f78f6c956..7de26e98ad 100644 --- a/doc/src/sgml/runtime.sgml +++ b/doc/src/sgml/runtime.sgml @@ -720,13 +720,13 @@ psql: could not connect to server: No such file or directory SEMMNI Maximum number of semaphore identifiers (i.e., sets) - at least ceil((max_connections + autovacuum_max_workers + max_worker_processes + 5) / 16) plus room for other applications + at least ceil((max_connections + autovacuum_max_workers + wax_wal_senders + max_worker_processes + 5) / 16) plus room for other applications SEMMNS Maximum number of semaphores system-wide - ceil((max_connections + autovacuum_max_workers + max_worker_processes + 5) / 16) * 17 plus room for other applications + ceil((max_connections + autovacuum_max_workers + max_wal_senders + max_worker_processes + 5) / 16) * 17 plus room for other applications @@ -785,13 +785,13 @@ psql: could not connect to server: No such file or directory other applications. The maximum number of semaphores in the system is set by SEMMNS, which consequently must be at least as high as max_connections plus - autovacuum_max_workers plus max_worker_processes, - plus one extra for each 16 + autovacuum_max_workers plus max_wal_senders, + plus max_worker_processes, plus one extra for each 16 allowed connections plus workers (see the formula in ). The parameter SEMMNI determines the limit on the number of semaphore sets that can exist on the system at one time. Hence this parameter must be at - least ceil((max_connections + autovacuum_max_workers + max_worker_processes + 5) / 16). + least ceil((max_connections + autovacuum_max_workers + max_wal_senders + max_worker_processes + 5) / 16). Lowering the number of allowed connections is a temporary workaround for failures, which are usually confusingly worded No space diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c index 7f73251696..0ad4454a8c 100644 --- a/src/backend/access/rmgrdesc/xlogdesc.c +++ b/src/backend/access/rmgrdesc/xlogdesc.c @@ -110,10 +110,11 @@ xlog_desc(StringInfo buf, XLogReaderState *record) } appendStringInfo(buf, "max_connections=%d max_worker_processes=%d " - "max_prepared_xacts=%d max_locks_per_xact=%d " - "wal_level=%s wal_log_hints=%s " - "track_commit_timestamp=%s", + "max_wal_senders=%d max_prepared_xacts=%d " + "max_locks_per_xact=%d wal_level=%s " + "wal_log_hints=%s track_commit_timestamp=%s", xlrec.MaxConnections, + xlrec.max_wal_senders, xlrec.max_worker_processes, xlrec.max_prepared_xacts, xlrec.max_locks_per_xact, diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index a9f3272849..ecd12fc53a 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -5257,6 +5257,7 @@ BootStrapXLOG(void) /* Set important parameter values for use when replaying WAL */ ControlFile->MaxConnections = MaxConnections; ControlFile->max_worker_processes = max_worker_processes; + ControlFile->max_wal_senders = max_wal_senders; ControlFile->max_prepared_xacts = max_prepared_xacts; ControlFile->max_locks_per_xact = max_locks_per_xact; ControlFile->wal_level = wal_level; @@ -6170,6 +6171,9 @@ CheckRequiredParameterValues(void) RecoveryRequiresIntParameter("max_worker_processes", max_worker_processes, ControlFile->max_worker_processes); + RecoveryRequiresIntParameter("max_wal_senders", + max_wal_senders, + ControlFile->max_wal_senders); RecoveryRequiresIntParameter("max_prepared_transactions", max_prepared_xacts, ControlFile->max_prepared_xacts); @@ -9460,6 +9464,7 @@ XLogReportParameters(void) wal_log_hints != ControlFile->wal_log_hints || MaxConnections != ControlFile->MaxConnections || max_worker_processes != ControlFile->max_worker_processes || + max_wal_senders != ControlFile->max_wal_senders || max_prepared_xacts != ControlFile->max_prepared_xacts || max_locks_per_xact != ControlFile->max_locks_per_xact || track_commit_timestamp != ControlFile->track_commit_timestamp) @@ -9478,6 +9483,7 @@ XLogReportParameters(void) xlrec.MaxConnections = MaxConnections; xlrec.max_worker_processes = max_worker_processes; + xlrec.max_wal_senders = max_wal_senders; xlrec.max_prepared_xacts = max_prepared_xacts; xlrec.max_locks_per_xact = max_locks_per_xact; xlrec.wal_level = wal_level; @@ -9493,6 +9499,7 @@ XLogReportParameters(void) ControlFile->MaxConnections = MaxConnections; ControlFile->max_worker_processes = max_worker_processes; + ControlFile->max_wal_senders = max_wal_senders; ControlFile->max_prepared_xacts = max_prepared_xacts; ControlFile->max_locks_per_xact = max_locks_per_xact; ControlFile->wal_level = wal_level; @@ -9896,6 +9903,7 @@ xlog_redo(XLogReaderState *record) LWLockAcquire(ControlFileLock, LW_EXCLUSIVE); ControlFile->MaxConnections = xlrec.MaxConnections; ControlFile->max_worker_processes = xlrec.max_worker_processes; + ControlFile->max_wal_senders = xlrec.max_wal_senders; ControlFile->max_prepared_xacts = xlrec.max_prepared_xacts; ControlFile->max_locks_per_xact = xlrec.max_locks_per_xact; ControlFile->wal_level = xlrec.wal_level; @@ -9927,7 +9935,7 @@ xlog_redo(XLogReaderState *record) UpdateControlFile(); LWLockRelease(ControlFileLock); - /* Check to see if any changes to max_connections give problems */ + /* Check to see if any parameter change gives a problem on recovery */ CheckRequiredParameterValues(); } else if (info == XLOG_FPW_CHANGE) diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 40a0222220..ccea231e98 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -885,11 +885,11 @@ PostmasterMain(int argc, char *argv[]) /* * Check for invalid combinations of GUC settings. */ - if (ReservedBackends + max_wal_senders >= MaxConnections) + if (ReservedBackends >= MaxConnections) { - write_stderr("%s: superuser_reserved_connections (%d) plus max_wal_senders (%d) must be less than max_connections (%d)\n", + write_stderr("%s: superuser_reserved_connections (%d) must be less than max_connections (%d)\n", progname, - ReservedBackends, max_wal_senders, MaxConnections); + ReservedBackends, MaxConnections); ExitPostmaster(1); } if (XLogArchiveMode > ARCHIVE_MODE_OFF && wal_level == WAL_LEVEL_MINIMAL) @@ -5532,7 +5532,7 @@ int MaxLivePostmasterChildren(void) { return 2 * (MaxConnections + autovacuum_max_workers + 1 + - max_worker_processes); + max_wal_senders + max_worker_processes); } /* diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c index 2d2eb23eb7..9b143f361b 100644 --- a/src/backend/replication/walsender.c +++ b/src/backend/replication/walsender.c @@ -2273,8 +2273,8 @@ InitWalSenderSlot(void) Assert(MyWalSnd == NULL); /* - * Find a free walsender slot and reserve it. If this fails, we must be - * out of WalSnd structures. + * Find a free walsender slot and reserve it. This must not fail due to + * the prior check for free WAL senders in InitProcess(). */ for (i = 0; i < max_wal_senders; i++) { @@ -2310,12 +2310,8 @@ InitWalSenderSlot(void) break; } } - if (MyWalSnd == NULL) - ereport(FATAL, - (errcode(ERRCODE_TOO_MANY_CONNECTIONS), - errmsg("number of requested standby connections " - "exceeds max_wal_senders (currently %d)", - max_wal_senders))); + + Assert(MyWalSnd != NULL); /* Arrange to clean up at walsender exit */ on_shmem_exit(WalSndKill, 0); diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c index 89c80fb687..0da5b19719 100644 --- a/src/backend/storage/lmgr/proc.c +++ b/src/backend/storage/lmgr/proc.c @@ -43,6 +43,7 @@ #include "postmaster/autovacuum.h" #include "replication/slot.h" #include "replication/syncrep.h" +#include "replication/walsender.h" #include "storage/condition_variable.h" #include "storage/standby.h" #include "storage/ipc.h" @@ -147,8 +148,9 @@ 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, max_worker_processes, or autovacuum_max_workers higher - * than his kernel will support, he'll find out sooner rather than later. + * MaxConnections, max_worker_processes, max_wal_senders, 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 @@ -180,6 +182,7 @@ InitProcGlobal(void) ProcGlobal->freeProcs = NULL; ProcGlobal->autovacFreeProcs = NULL; ProcGlobal->bgworkerFreeProcs = NULL; + ProcGlobal->walsenderFreeProcs = NULL; ProcGlobal->startupProc = NULL; ProcGlobal->startupProcPid = 0; ProcGlobal->startupBufferPinWaitBufId = -1; @@ -253,13 +256,20 @@ InitProcGlobal(void) ProcGlobal->autovacFreeProcs = &procs[i]; procs[i].procgloballist = &ProcGlobal->autovacFreeProcs; } - else if (i < MaxBackends) + else if (i < MaxConnections + autovacuum_max_workers + 1 + max_worker_processes) { /* PGPROC for bgworker, add to bgworkerFreeProcs list */ procs[i].links.next = (SHM_QUEUE *) ProcGlobal->bgworkerFreeProcs; ProcGlobal->bgworkerFreeProcs = &procs[i]; procs[i].procgloballist = &ProcGlobal->bgworkerFreeProcs; } + else if (i < MaxBackends) + { + /* PGPROC for walsender, add to walsenderFreeProcs list */ + procs[i].links.next = (SHM_QUEUE *) ProcGlobal->walsenderFreeProcs; + ProcGlobal->walsenderFreeProcs = &procs[i]; + procs[i].procgloballist = &ProcGlobal->walsenderFreeProcs; + } /* Initialize myProcLocks[] shared memory queues. */ for (j = 0; j < NUM_LOCK_PARTITIONS; j++) @@ -311,6 +321,8 @@ InitProcess(void) procgloballist = &ProcGlobal->autovacFreeProcs; else if (IsBackgroundWorker) procgloballist = &ProcGlobal->bgworkerFreeProcs; + else if (am_walsender) + procgloballist = &ProcGlobal->walsenderFreeProcs; else procgloballist = &ProcGlobal->freeProcs; @@ -341,6 +353,11 @@ InitProcess(void) * in the autovacuum case? */ SpinLockRelease(ProcStructLock); + if (am_walsender) + ereport(FATAL, + (errcode(ERRCODE_TOO_MANY_CONNECTIONS), + errmsg("number of requested standby connections exceeds max_wal_senders (currently %d)", + max_wal_senders))); ereport(FATAL, (errcode(ERRCODE_TOO_MANY_CONNECTIONS), errmsg("sorry, too many clients already"))); diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index c0b6231458..a5ee209f91 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -527,7 +527,7 @@ InitializeMaxBackends(void) /* the extra unit accounts for the autovacuum launcher */ MaxBackends = MaxConnections + autovacuum_max_workers + 1 + - max_worker_processes; + max_worker_processes + max_wal_senders; /* internal error because the values were all checked previously */ if (MaxBackends > MAX_BACKENDS) @@ -811,12 +811,11 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username, } /* - * The last few connection slots are reserved for superusers. Although - * replication connections currently require superuser privileges, we - * don't allow them to consume the reserved slots, which are intended for - * interactive use. + * The last few connection slots are reserved for superusers. Replication + * connections are drawn from slots reserved with max_wal_senders and not + * limited by max_connections or superuser_reserved_connections. */ - if ((!am_superuser || am_walsender) && + if (!am_superuser && !am_walsender && ReservedBackends > 0 && !HaveNFreeProcs(ReservedBackends)) ereport(FATAL, diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index ea5444c6f1..41d477165c 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -187,6 +187,7 @@ 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_max_wal_senders(int *newval, void **extra, GucSource source); static bool check_autovacuum_work_mem(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); @@ -2090,7 +2091,7 @@ static struct config_int ConfigureNamesInt[] = }, { - /* see max_connections and max_wal_senders */ + /* see max_connections */ {"superuser_reserved_connections", PGC_POSTMASTER, CONN_AUTH_SETTINGS, gettext_noop("Sets the number of connection slots reserved for superusers."), NULL @@ -2608,14 +2609,13 @@ static struct config_int ConfigureNamesInt[] = }, { - /* see max_connections and superuser_reserved_connections */ {"max_wal_senders", PGC_POSTMASTER, REPLICATION_SENDING, gettext_noop("Sets the maximum number of simultaneously running WAL sender processes."), NULL }, &max_wal_senders, 10, 0, MAX_BACKENDS, - NULL, NULL, NULL + check_max_wal_senders, NULL, NULL }, { @@ -10911,7 +10911,7 @@ static bool check_maxconnections(int *newval, void **extra, GucSource source) { if (*newval + autovacuum_max_workers + 1 + - max_worker_processes > MAX_BACKENDS) + max_worker_processes + max_wal_senders > MAX_BACKENDS) return false; return true; } @@ -10919,7 +10919,17 @@ check_maxconnections(int *newval, void **extra, GucSource source) static bool check_autovacuum_max_workers(int *newval, void **extra, GucSource source) { - if (MaxConnections + *newval + 1 + max_worker_processes > MAX_BACKENDS) + if (MaxConnections + *newval + 1 + + max_worker_processes + max_wal_senders > MAX_BACKENDS) + return false; + return true; +} + +static bool +check_max_wal_senders(int *newval, void **extra, GucSource source) +{ + if (MaxConnections + autovacuum_max_workers + 1 + + max_worker_processes + *newval > MAX_BACKENDS) return false; return true; } @@ -10950,7 +10960,8 @@ check_autovacuum_work_mem(int *newval, void **extra, GucSource source) static bool check_max_worker_processes(int *newval, void **extra, GucSource source) { - if (MaxConnections + autovacuum_max_workers + 1 + *newval > MAX_BACKENDS) + if (MaxConnections + autovacuum_max_workers + 1 + + *newval + max_wal_senders > MAX_BACKENDS) return false; return true; } diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c index 1e1fd85b0b..1aa1db218a 100644 --- a/src/bin/pg_controldata/pg_controldata.c +++ b/src/bin/pg_controldata/pg_controldata.c @@ -304,6 +304,8 @@ main(int argc, char *argv[]) ControlFile->MaxConnections); printf(_("max_worker_processes setting: %d\n"), ControlFile->max_worker_processes); + printf(_("max_wal_senders setting: %d\n"), + ControlFile->max_wal_senders); printf(_("max_prepared_xacts setting: %d\n"), ControlFile->max_prepared_xacts); printf(_("max_locks_per_xact setting: %d\n"), diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c index 562c27904f..2af8713216 100644 --- a/src/bin/pg_resetwal/pg_resetwal.c +++ b/src/bin/pg_resetwal/pg_resetwal.c @@ -728,6 +728,7 @@ GuessControlValues(void) ControlFile.wal_log_hints = false; ControlFile.track_commit_timestamp = false; ControlFile.MaxConnections = 100; + ControlFile.max_wal_senders = 10; ControlFile.max_worker_processes = 8; ControlFile.max_prepared_xacts = 0; ControlFile.max_locks_per_xact = 64; @@ -955,6 +956,7 @@ RewriteControlFile(void) ControlFile.wal_log_hints = false; ControlFile.track_commit_timestamp = false; ControlFile.MaxConnections = 100; + ControlFile.max_wal_senders = 10; ControlFile.max_worker_processes = 8; ControlFile.max_prepared_xacts = 0; ControlFile.max_locks_per_xact = 64; diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index 3c86037251..42d1065d1e 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -31,7 +31,7 @@ /* * Each page of XLOG file has a header like this: */ -#define XLOG_PAGE_MAGIC 0xD098 /* can be used as WAL version indicator */ +#define XLOG_PAGE_MAGIC 0xD099 /* can be used as WAL version indicator */ typedef struct XLogPageHeaderData { @@ -226,6 +226,7 @@ typedef struct xl_parameter_change { int MaxConnections; int max_worker_processes; + int max_wal_senders; int max_prepared_xacts; int max_locks_per_xact; int wal_level; diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h index a4aa83bae8..a3910a5f99 100644 --- a/src/include/catalog/pg_control.h +++ b/src/include/catalog/pg_control.h @@ -21,7 +21,7 @@ /* Version identifier for this pg_control format */ -#define PG_CONTROL_VERSION 1100 +#define PG_CONTROL_VERSION 1200 /* Nonce key length, see below */ #define MOCK_AUTH_NONCE_LEN 32 @@ -177,6 +177,7 @@ typedef struct ControlFileData bool wal_log_hints; int MaxConnections; int max_worker_processes; + int max_wal_senders; int max_prepared_xacts; int max_locks_per_xact; bool track_commit_timestamp; diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index d203acbb30..1cee7db89d 100644 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -255,6 +255,8 @@ typedef struct PROC_HDR PGPROC *autovacFreeProcs; /* Head of list of bgworker free PGPROC structures */ PGPROC *bgworkerFreeProcs; + /* Head of list of walsender free PGPROC structures */ + PGPROC *walsenderFreeProcs; /* First pgproc waiting for group XID clear */ pg_atomic_uint32 procArrayGroupFirst; /* First pgproc waiting for group transaction status update */