]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Add new GUC reserved_connections.
authorRobert Haas <rhaas@postgresql.org>
Fri, 20 Jan 2023 20:36:36 +0000 (15:36 -0500)
committerRobert Haas <rhaas@postgresql.org>
Fri, 20 Jan 2023 20:39:13 +0000 (15:39 -0500)
This provides a way to reserve connection slots for non-superusers.
The slots reserved via the new GUC are available only to users who
have the new predefined role pg_use_reserved_connections.
superuser_reserved_connections remains as a final reserve in case
reserved_connections has been exhausted.

Patch by Nathan Bossart. Reviewed by Tushar Ahuja and by me.

Discussion: http://postgr.es/m/20230119194601.GA4105788@nathanxps13

doc/src/sgml/config.sgml
doc/src/sgml/user-manag.sgml
src/backend/postmaster/postmaster.c
src/backend/storage/lmgr/proc.c
src/backend/utils/init/postinit.c
src/backend/utils/misc/guc_tables.c
src/backend/utils/misc/postgresql.conf.sample
src/include/catalog/pg_authid.dat
src/include/postmaster/postmaster.h
src/include/storage/proc.h

index e019a1aac9937dbf9c7053d71c98d70c5d553825..dc9b78b0b7d4cb0b5a3fde948acfdc1606001444 100644 (file)
@@ -708,6 +708,37 @@ include_dir 'conf.d'
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-reserved-connections" xreflabel="reserved_connections">
+      <term><varname>reserved_connections</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>reserved_connections</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Determines the number of connection <quote>slots</quote> that are
+        reserved for connections by roles with privileges of the
+        <link linkend="predefined-roles-table"><literal>pg_used_reserved_connections</literal></link>
+        role.  Whenever the number of free connection slots is greater than
+        <xref linkend="guc-superuser-reserved-connections"/> but less than or
+        equal to the sum of <varname>superuser_reserved_connections</varname>
+        and <varname>reserved_connections</varname>, new connections will be
+        accepted only for superusers and roles with privileges of
+        <literal>pg_use_reserved_connections</literal>.  If
+        <varname>superuser_reserved_connections</varname> or fewer connection
+        slots are available, new connections will be accepted only for
+        superusers.
+       </para>
+
+       <para>
+        The default value is zero connections.  The value must be less than
+        <varname>max_connections</varname> minus
+        <varname>superuser_reserved_connections</varname>.  This parameter can
+        only be set at server start.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-superuser-reserved-connections"
      xreflabel="superuser_reserved_connections">
       <term><varname>superuser_reserved_connections</varname>
@@ -725,12 +756,16 @@ include_dir 'conf.d'
         number of active concurrent connections is at least
         <varname>max_connections</varname> minus
         <varname>superuser_reserved_connections</varname>, new
-        connections will be accepted only for superusers.
+        connections will be accepted only for superusers.  The connection slots
+        reserved by this parameter are intended as final reserve for emergency
+        use after the slots reserved by
+        <xref linkend="guc-reserved-connections"/> have been exhausted.
        </para>
 
        <para>
         The default value is three connections. The value must be less
-        than <varname>max_connections</varname>.
+        than <varname>max_connections</varname> minus
+        <varname>reserved_connections</varname>.
         This parameter can only be set at server start.
        </para>
       </listitem>
index 71a2d8f298572d49c00ec058b0a727bd23743ec0..002c1e3affb5c60dc814ad1ebadba03b48e26d11 100644 (file)
@@ -689,6 +689,11 @@ DROP ROLE doomed_role;
        and <link linkend="sql-lock"><command>LOCK TABLE</command></link> on all
        relations.</entry>
       </row>
+      <row>
+       <entry>pg_use_reserved_connections</entry>
+       <entry>Allow use of connection slots reserved via
+       <xref linkend="guc-reserved-connections"/>.</entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
index 3f799c4ac82b4953c80bf0d41f5b146417f401ca..aca1ef91b5782b37cc794219d104be152e8bfbcb 100644 (file)
@@ -205,14 +205,24 @@ char         *ListenAddresses;
 
 /*
  * SuperuserReservedConnections is the number of backends reserved for
- * superuser use.  This number is taken out of the pool size given by
- * MaxConnections so number of backend slots available to non-superusers is
- * (MaxConnections - SuperuserReservedConnections).  Note what this really
- * means is "if there are <= SuperuserReservedConnections connections
- * available, only superusers can make new connections" --- pre-existing
- * superuser connections don't count against the limit.
+ * superuser use, and ReservedConnections is the number of backends reserved
+ * for use by roles with privileges of the pg_use_reserved_connections
+ * predefined role.  These are taken out of the pool of MaxConnections backend
+ * slots, so the number of backend slots available for roles that are neither
+ * superuser nor have privileges of pg_use_reserved_connections is
+ * (MaxConnections - SuperuserReservedConnections - ReservedConnections).
+ *
+ * If the number of remaining slots is less than or equal to
+ * SuperuserReservedConnections, only superusers can make new connections.  If
+ * the number of remaining slots is greater than SuperuserReservedConnections
+ * but less than or equal to
+ * (SuperuserReservedConnections + ReservedConnections), only superusers and
+ * roles with privileges of pg_use_reserved_connections can make new
+ * connections.  Note that pre-existing superuser and
+ * pg_use_reserved_connections connections don't count against the limits.
  */
 int                    SuperuserReservedConnections;
+int                    ReservedConnections;
 
 /* The socket(s) we're listening to. */
 #define MAXLISTEN      64
@@ -908,11 +918,12 @@ PostmasterMain(int argc, char *argv[])
        /*
         * Check for invalid combinations of GUC settings.
         */
-       if (SuperuserReservedConnections >= MaxConnections)
+       if (SuperuserReservedConnections + ReservedConnections >= MaxConnections)
        {
-               write_stderr("%s: superuser_reserved_connections (%d) must be less than max_connections (%d)\n",
+               write_stderr("%s: superuser_reserved_connections (%d) plus reserved_connections (%d) must be less than max_connections (%d)\n",
                                         progname,
-                                        SuperuserReservedConnections, MaxConnections);
+                                        SuperuserReservedConnections, ReservedConnections,
+                                        MaxConnections);
                ExitPostmaster(1);
        }
        if (XLogArchiveMode > ARCHIVE_MODE_OFF && wal_level == WAL_LEVEL_MINIMAL)
index f8ac4edd6ffe00450603bd3c02aa6d613f82ecc4..22b4278610c03dfbbb2053f3b18582356a3482f2 100644 (file)
@@ -645,27 +645,33 @@ GetStartupBufferPinWaitBufId(void)
 }
 
 /*
- * Check whether there are at least N free PGPROC objects.
+ * Check whether there are at least N free PGPROC objects.  If false is
+ * returned, *nfree will be set to the number of free PGPROC objects.
+ * Otherwise, *nfree will be set to n.
  *
  * Note: this is designed on the assumption that N will generally be small.
  */
 bool
-HaveNFreeProcs(int n)
+HaveNFreeProcs(int n, int *nfree)
 {
        dlist_iter      iter;
 
+       Assert(n > 0);
+       Assert(nfree);
+
        SpinLockAcquire(ProcStructLock);
 
+       *nfree = 0;
        dlist_foreach(iter, &ProcGlobal->freeProcs)
        {
-               n--;
-               if (n == 0)
+               (*nfree)++;
+               if (*nfree == n)
                        break;
        }
 
        SpinLockRelease(ProcStructLock);
 
-       return (n <= 0);
+       return (*nfree == n);
 }
 
 /*
index 40f145e0ab1966f6367a453a98fb386a062e8689..2f07ca7a0e1dd8694dabf8fe258950b85ff600f2 100644 (file)
@@ -719,6 +719,7 @@ InitPostgres(const char *in_dbname, Oid dboid,
        bool            am_superuser;
        char       *fullpath;
        char            dbname[NAMEDATALEN];
+       int                     nfree = 0;
 
        elog(DEBUG3, "InitPostgres");
 
@@ -922,16 +923,30 @@ InitPostgres(const char *in_dbname, Oid dboid,
        }
 
        /*
-        * 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.
+        * The last few connection slots are reserved for superusers and roles with
+        * privileges of pg_use_reserved_connections.  Replication connections are
+        * drawn from slots reserved with max_wal_senders and are not limited by
+        * max_connections, superuser_reserved_connections, or
+        * reserved_connections.
+        *
+        * Note: At this point, the new backend has already claimed a proc struct,
+        * so we must check whether the number of free slots is strictly less than
+        * the reserved connection limits.
         */
        if (!am_superuser && !am_walsender &&
-               SuperuserReservedConnections > 0 &&
-               !HaveNFreeProcs(SuperuserReservedConnections))
-               ereport(FATAL,
-                               (errcode(ERRCODE_TOO_MANY_CONNECTIONS),
-                                errmsg("remaining connection slots are reserved for superusers")));
+               (SuperuserReservedConnections + ReservedConnections) > 0 &&
+               !HaveNFreeProcs(SuperuserReservedConnections + ReservedConnections, &nfree))
+       {
+               if (nfree < SuperuserReservedConnections)
+                       ereport(FATAL,
+                                       (errcode(ERRCODE_TOO_MANY_CONNECTIONS),
+                                        errmsg("remaining connection slots are reserved for superusers")));
+
+               if (!has_privs_of_role(GetUserId(), ROLE_PG_USE_RESERVED_CONNECTIONS))
+                       ereport(FATAL,
+                                       (errcode(ERRCODE_TOO_MANY_CONNECTIONS),
+                                        errmsg("remaining connection slots are reserved for roles with privileges of pg_use_reserved_connections")));
+       }
 
        /* Check replication permissions needed for walsender processes. */
        if (am_walsender)
index 0fa9fdd3c5866791c39b2c5a70d0fe01625bef3c..e1753a40fa9496ba3b3024509f35844105062e18 100644 (file)
@@ -2168,6 +2168,17 @@ struct config_int ConfigureNamesInt[] =
                NULL, NULL, NULL
        },
 
+       {
+               {"reserved_connections", PGC_POSTMASTER, CONN_AUTH_SETTINGS,
+                       gettext_noop("Sets the number of connection slots reserved for roles "
+                                                "with privileges of pg_use_reserved_connections."),
+                       NULL
+               },
+               &ReservedConnections,
+               0, 0, MAX_BACKENDS,
+               NULL, NULL, NULL
+       },
+
        {
                {"min_dynamic_shared_memory", PGC_POSTMASTER, RESOURCES_MEM,
                        gettext_noop("Amount of dynamic shared memory reserved at startup."),
index 4cceda416222692b8230e57a63a4d863b97b9849..d06074b86f6de8fce9dedffc1207685dcf5900e8 100644 (file)
@@ -63,6 +63,7 @@
                                        # (change requires restart)
 #port = 5432                           # (change requires restart)
 #max_connections = 100                 # (change requires restart)
+#reserved_connections = 0              # (change requires restart)
 #superuser_reserved_connections = 3    # (change requires restart)
 #unix_socket_directories = '/tmp'      # comma-separated list of directories
                                        # (change requires restart)
index 2a2fee7d28346e6890b4adc3a851172027d7e283..f2e5663c9fec1165d79df2363cd9ee68b62d61ff 100644 (file)
   rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f',
   rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1',
   rolpassword => '_null_', rolvaliduntil => '_null_' },
+{ oid => '4550', oid_symbol => 'ROLE_PG_USE_RESERVED_CONNECTIONS',
+  rolname => 'pg_use_reserved_connections', rolsuper => 'f', rolinherit => 't',
+  rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f',
+  rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1',
+  rolpassword => '_null_', rolvaliduntil => '_null_' },
 
 ]
index 0e4b8ded34414811e7ea1b1a623dc207be620140..3b3889c58c049be0aec3d89c7532a7b5da914862 100644 (file)
@@ -16,6 +16,7 @@
 /* GUC options */
 extern PGDLLIMPORT bool EnableSSL;
 extern PGDLLIMPORT int SuperuserReservedConnections;
+extern PGDLLIMPORT int ReservedConnections;
 extern PGDLLIMPORT int PostPortNumber;
 extern PGDLLIMPORT int Unix_socket_permissions;
 extern PGDLLIMPORT char *Unix_socket_group;
index dd45b8ee9b7424a9b56d08fa19054352c5753f9e..4258cd92c9c0b76764e1cb000517bb8e23f1d308 100644 (file)
@@ -445,7 +445,7 @@ extern void InitAuxiliaryProcess(void);
 extern void SetStartupBufferPinWaitBufId(int bufid);
 extern int     GetStartupBufferPinWaitBufId(void);
 
-extern bool HaveNFreeProcs(int n);
+extern bool HaveNFreeProcs(int n, int *nfree);
 extern void ProcReleaseLocks(bool isCommit);
 
 extern ProcWaitStatus ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable);