From f1169ab501ce90e035a7c6489013a1d4c250ac92 Mon Sep 17 00:00:00 2001 From: Andrew Dunstan <andrew@dunslane.net> Date: Wed, 1 Feb 2017 17:52:35 -0500 Subject: [PATCH] Don't count background workers against a user's connection limit. Doing so doesn't seem to be within the purpose of the per user connection limits, and has particularly unfortunate effects in conjunction with parallel queries. Backpatch to 9.6 where parallel queries were introduced. David Rowley, reviewed by Robert Haas and Albe Laurenz. --- doc/src/sgml/ref/create_database.sgml | 3 ++- doc/src/sgml/ref/create_role.sgml | 5 +++- src/backend/access/transam/twophase.c | 1 + src/backend/storage/ipc/procarray.c | 34 +++++++++++++++++++++++++++ src/backend/storage/lmgr/proc.c | 2 ++ src/backend/utils/init/postinit.c | 2 +- src/include/storage/proc.h | 2 ++ src/include/storage/procarray.h | 1 + 8 files changed, 47 insertions(+), 3 deletions(-) diff --git a/doc/src/sgml/ref/create_database.sgml b/doc/src/sgml/ref/create_database.sgml index cf33746c1e2..cf0d53b3014 100644 --- a/doc/src/sgml/ref/create_database.sgml +++ b/doc/src/sgml/ref/create_database.sgml @@ -258,7 +258,8 @@ CREATE DATABASE <replaceable class="PARAMETER">name</replaceable> The <literal>CONNECTION LIMIT</> option is only enforced approximately; if two new sessions start at about the same time when just one connection <quote>slot</> remains for the database, it is possible that - both will fail. Also, the limit is not enforced against superusers. + both will fail. Also, the limit is not enforced against superusers or + background worker processes. </para> </refsect1> diff --git a/doc/src/sgml/ref/create_role.sgml b/doc/src/sgml/ref/create_role.sgml index a3b8ed9ab4c..2ae576ede69 100644 --- a/doc/src/sgml/ref/create_role.sgml +++ b/doc/src/sgml/ref/create_role.sgml @@ -198,7 +198,10 @@ CREATE ROLE <replaceable class="PARAMETER">name</replaceable> [ [ WITH ] <replac <listitem> <para> If role can log in, this specifies how many concurrent connections - the role can make. -1 (the default) means no limit. + the role can make. -1 (the default) means no limit. Note that only + normal connections are counted towards this limit. Neither prepared + transactions nor background worker connections are counted towards + this limit. </para> </listitem> </varlistentry> diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c index 5b72c1dcf57..6fde2bd8bfd 100644 --- a/src/backend/access/transam/twophase.c +++ b/src/backend/access/transam/twophase.c @@ -420,6 +420,7 @@ MarkAsPreparing(TransactionId xid, const char *gid, proc->backendId = InvalidBackendId; proc->databaseId = databaseid; proc->roleId = owner; + proc->isBackgroundWorker = false; proc->lwWaiting = false; proc->lwWaitMode = 0; proc->waitLock = NULL; diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index 3f47b984eef..cd14667c16c 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -2744,6 +2744,38 @@ CountDBBackends(Oid databaseid) return count; } +/* + * CountDBConnections --- counts database backends ignoring any background + * worker processes + */ +int +CountDBConnections(Oid databaseid) +{ + ProcArrayStruct *arrayP = procArray; + int count = 0; + int index; + + LWLockAcquire(ProcArrayLock, LW_SHARED); + + for (index = 0; index < arrayP->numProcs; index++) + { + int pgprocno = arrayP->pgprocnos[index]; + volatile PGPROC *proc = &allProcs[pgprocno]; + + if (proc->pid == 0) + continue; /* do not count prepared xacts */ + if (proc->isBackgroundWorker) + continue; /* do not count background workers */ + if (!OidIsValid(databaseid) || + proc->databaseId == databaseid) + count++; + } + + LWLockRelease(ProcArrayLock); + + return count; +} + /* * CancelDBBackends --- cancel backends that are using specified database */ @@ -2803,6 +2835,8 @@ CountUserBackends(Oid roleid) if (proc->pid == 0) continue; /* do not count prepared xacts */ + if (proc->isBackgroundWorker) + continue; /* do not count background workers */ if (proc->roleId == roleid) count++; } diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c index 1b836f7c0ac..8f467bef504 100644 --- a/src/backend/storage/lmgr/proc.c +++ b/src/backend/storage/lmgr/proc.c @@ -370,6 +370,7 @@ InitProcess(void) MyProc->backendId = InvalidBackendId; MyProc->databaseId = InvalidOid; MyProc->roleId = InvalidOid; + MyProc->isBackgroundWorker = IsBackgroundWorker; MyPgXact->delayChkpt = false; MyPgXact->vacuumFlags = 0; /* NB -- autovac launcher intentionally does not set IS_AUTOVACUUM */ @@ -542,6 +543,7 @@ InitAuxiliaryProcess(void) MyProc->backendId = InvalidBackendId; MyProc->databaseId = InvalidOid; MyProc->roleId = InvalidOid; + MyProc->isBackgroundWorker = IsBackgroundWorker; MyPgXact->delayChkpt = false; MyPgXact->vacuumFlags = 0; MyProc->lwWaiting = false; diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index 21fdc6df6b1..4d0a2a7bed3 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -350,7 +350,7 @@ CheckMyDatabase(const char *name, bool am_superuser) */ if (dbform->datconnlimit >= 0 && !am_superuser && - CountDBBackends(MyDatabaseId) > dbform->datconnlimit) + CountDBConnections(MyDatabaseId) > dbform->datconnlimit) ereport(FATAL, (errcode(ERRCODE_TOO_MANY_CONNECTIONS), errmsg("too many connections for database \"%s\"", diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index 398fa8afdee..5f38fa6b4f1 100644 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -103,6 +103,8 @@ struct PGPROC Oid databaseId; /* OID of database this backend is using */ Oid roleId; /* OID of role using this backend */ + bool isBackgroundWorker; /* true if background worker. */ + /* * While in hot standby mode, shows that a conflict signal has been sent * for the current transaction. Set/cleared while holding ProcArrayLock, diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h index 0d5027fa64f..9d5a13eb3b2 100644 --- a/src/include/storage/procarray.h +++ b/src/include/storage/procarray.h @@ -73,6 +73,7 @@ extern pid_t CancelVirtualTransaction(VirtualTransactionId vxid, ProcSignalReaso extern bool MinimumActiveBackends(int min); extern int CountDBBackends(Oid databaseid); +extern int CountDBConnections(Oid databaseid); extern void CancelDBBackends(Oid databaseid, ProcSignalReason sigmode, bool conflictPending); extern int CountUserBackends(Oid roleid); extern bool CountOtherDBBackends(Oid databaseId, -- GitLab