Don't count background workers against a user's connection limit.
authorAndrew Dunstan <andrew@dunslane.net>
Wed, 1 Feb 2017 22:52:35 +0000 (17:52 -0500)
committerAndrew Dunstan <andrew@dunslane.net>
Wed, 1 Feb 2017 23:02:43 +0000 (18:02 -0500)
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
doc/src/sgml/ref/create_role.sgml
src/backend/access/transam/twophase.c
src/backend/storage/ipc/procarray.c
src/backend/storage/lmgr/proc.c
src/backend/utils/init/postinit.c
src/include/storage/proc.h
src/include/storage/procarray.h

index cf33746c1e23c96f0b6e875400927c93a96ba03d..cf0d53b3014bda73cd561c88c9b747fb7aa772af 100644 (file)
@@ -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>
 
index a3b8ed9ab4c8e62bb6f963cbf1129a849d18e51c..2ae576ede696c16a1b7e9d52736d422b63b60fa7 100644 (file)
@@ -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>
index 5b72c1dcf57fae72e5eef00effac170d9f32ae25..6fde2bd8bfd95fdfe7ab2f2460f29bc054acd9bc 100644 (file)
@@ -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;
index 3f47b984eef920a19cca4e1b3a3ec4130fa288fa..cd14667c16c153ab1f0f27dff3ce2769dd60f8f9 100644 (file)
@@ -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++;
    }
index 1b836f7c0acf53e3c4da393785a754e4c086562e..8f467bef504b66b45ca5790da234687df0798e96 100644 (file)
@@ -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;
index 21fdc6df6b121785a8c60712e02bfdff67174d1c..4d0a2a7bed3eccd5d033e4fca627f3d9e715f545 100644 (file)
@@ -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\"",
index 398fa8afdeec431835a5452276d6ab06b65a9fb6..5f38fa6b4f1f14f41bb5cde599d74b6e093d714b 100644 (file)
@@ -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,
index 0d5027fa64f0646fa7f8e014f7e30211569d24a7..9d5a13eb3b2aacde6380346d9903843a568a891e 100644 (file)
@@ -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,