diff --git a/doc/src/sgml/ref/create_database.sgml b/doc/src/sgml/ref/create_database.sgml index cf33746c1e23c96f0b6e875400927c93a96ba03d..cf0d53b3014bda73cd561c88c9b747fb7aa772af 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 a3b8ed9ab4c8e62bb6f963cbf1129a849d18e51c..2ae576ede696c16a1b7e9d52736d422b63b60fa7 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 5b72c1dcf57fae72e5eef00effac170d9f32ae25..6fde2bd8bfd95fdfe7ab2f2460f29bc054acd9bc 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 3f47b984eef920a19cca4e1b3a3ec4130fa288fa..cd14667c16c153ab1f0f27dff3ce2769dd60f8f9 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 1b836f7c0acf53e3c4da393785a754e4c086562e..8f467bef504b66b45ca5790da234687df0798e96 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 21fdc6df6b121785a8c60712e02bfdff67174d1c..4d0a2a7bed3eccd5d033e4fca627f3d9e715f545 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 398fa8afdeec431835a5452276d6ab06b65a9fb6..5f38fa6b4f1f14f41bb5cde599d74b6e093d714b 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 0d5027fa64f0646fa7f8e014f7e30211569d24a7..9d5a13eb3b2aacde6380346d9903843a568a891e 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,