Code review for superuser_reserved_connections patch. Don't try to do
authorTom Lane <[email protected]>
Thu, 21 Nov 2002 06:36:27 +0000 (06:36 +0000)
committerTom Lane <[email protected]>
Thu, 21 Nov 2002 06:36:27 +0000 (06:36 +0000)
database access outside a transaction; revert bogus performance improvement
in SIBackendInit(); improve comments; add documentation (this part courtesy
Neil Conway).

doc/src/sgml/runtime.sgml
src/backend/postmaster/postmaster.c
src/backend/storage/ipc/sinval.c
src/backend/storage/ipc/sinvaladt.c
src/backend/utils/init/postinit.c

index 9e91e223effd09f5296998f7835ebdefc545fc53..6c0f8b27ebc454df68a7866dff714e00fcc2c9fc 100644 (file)
@@ -1866,6 +1866,28 @@ dynamic_library_path = '/usr/local/lib/postgresql:/home/my_project/lib:$libdir'
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><varname>SUPERUSER_RESERVED_CONNECTIONS</varname>
+      (<type>integer</type>)</term>
+      <listitem>
+       <para>
+        Determines the number of <quote>connection slots</quote> that
+        are reserved for connections by <productname>PostgreSQL</>
+        superusers.  At most <varname>max_connections</> connections can
+       ever be active simultaneously.  Whenever the number of active
+       concurrent connections is at least <varname>max_connections</> minus
+        <varname>superuser_reserved_connections</varname>, new connections
+       will be accepted only from superuser accounts.
+       </para>
+
+       <para>
+        The default value is 2. The value must be less than the value of
+        <varname>max_connections</varname>. This parameter can only be
+        set at server start.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><varname>TCPIP_SOCKET</varname> (<type>boolean</type>)</term>
       <listitem>
@@ -2903,24 +2925,25 @@ $ <userinput>kill -INT `head -1 /usr/local/pgsql/data/postmaster.pid`</userinput
   </para>
 
   <para>
-   With SSL support compiled in, the <productname>PostgreSQL</> server
-   can be started with SSL support by setting the parameter
-   <varname>ssl</varname> to on in
-   <filename>postgresql.conf</filename>. When starting in SSL mode,
-   the server will look for the files <filename>server.key</> and
-   <filename>server.crt</> in the data directory.  These files should
-   contain the server private key and certificate respectively. These
-   files must be set up correctly before an SSL-enabled server can
-   start. If the private key is protected with a passphrase, the
-   server will prompt for the passphrase and will not start until it
-   has been entered.
+   With <acronym>SSL</> support compiled in, the
+   <productname>PostgreSQL</> server can be started with
+   <acronym>SSL</> support by setting the parameter
+   <varname>ssl</varname> to on in <filename>postgresql.conf</>. When
+   starting in <acronym>SSL</> mode, the server will look for the
+   files <filename>server.key</> and <filename>server.crt</> in the
+   data directory.  These files should contain the server private key
+   and certificate respectively. These files must be set up correctly
+   before an <acronym>SSL</>-enabled server can start. If the private key is
+   protected with a passphrase, the server will prompt for the
+   passphrase and will not start until it has been entered.
   </para>
 
   <para>
-   The server will listen for both standard and SSL connections on the
-   same TCP/IP port, and will negotiate with any connecting client on
-   whether to use SSL. See <xref linkend="client-authentication"> about
-   how to force the server to only use of SSL for certain connections.
+   The server will listen for both standard and <acronym>SSL</>
+   connections on the same TCP/IP port, and will negotiate with any
+   connecting client on whether to use <acronym>SSL</>. See <xref
+   linkend="client-authentication"> about how to force the server to
+   require use of <acronym>SSL</> for certain connections.
   </para>
 
   <para>
index 135433804009fadb0594d85168440dddd715fb72..23c462e58512fc5af0f5e83fce0e61630aab08e1 100644 (file)
@@ -154,12 +154,11 @@ int                       MaxBackends = DEF_MAXBACKENDS;
 /*
  * ReservedBackends is the number of backends reserved for superuser use.
  * This number is taken out of the pool size given by MaxBackends so
- * number of backend slots available to none super users is
- * (MaxBackends - ReservedBackends). Note, existing super user
- * connections are not taken into account once this lower limit has
- * been reached, i.e. superuser connections made before the lower limit
- * is reached always count towards that limit and are not taken from
- * ReservedBackends.
+ * number of backend slots available to non-superusers is
+ * (MaxBackends - ReservedBackends).  Note what this really means is
+ * "if there are <= ReservedBackends connections available, only superusers
+ * can make new connections" --- pre-existing superuser connections don't
+ * count against the limit.
  */
 int                    ReservedBackends = 2;
 
@@ -566,7 +565,15 @@ PostmasterMain(int argc, char *argv[])
        }
 
        /*
-        * Check for invalid combinations of switches
+        * Now we can set the data directory, and then read postgresql.conf.
+        */
+       checkDataDir(potential_DataDir);        /* issues error messages */
+       SetDataDir(potential_DataDir);
+
+       ProcessConfigFile(PGC_POSTMASTER);
+
+       /*
+        * Check for invalid combinations of GUC settings.
         */
        if (NBuffers < 2 * MaxBackends || NBuffers < 16)
        {
@@ -579,16 +586,11 @@ PostmasterMain(int argc, char *argv[])
                ExitPostmaster(1);
        }
 
-       checkDataDir(potential_DataDir);        /* issues error messages */
-       SetDataDir(potential_DataDir);
-
-       ProcessConfigFile(PGC_POSTMASTER);
-
-       /*
-        * Force an exit if ReservedBackends is not less than MaxBackends.
-        */
        if (ReservedBackends >= MaxBackends)
-               elog(FATAL, "superuser_reserved_connections must be less than max_connections.");
+       {
+               postmaster_error("superuser_reserved_connections must be less than max_connections.");
+               ExitPostmaster(1);
+       }
 
        /*
         * Now that we are done processing the postmaster arguments, reset
index c022acbada017d65028f690d8e4b4ce6c81c4e00..5e761ee396013a7c7d18ab4967ca65bfb8b093a7 100644 (file)
@@ -542,12 +542,11 @@ BackendIdGetProc(BackendId procId)
 /*
  * CountEmptyBackendSlots - count empty slots in backend process table
  *
- * Doesn't count since the procState array could be large and we've already
- * allowed for that by running a freeBackends counter in the SI segment.
- * Unlike CountActiveBackends() we do not need to interrogate the
- * backends to determine the free slot count.
- * Goes for a lock despite being a trival look up in case other backends
- * are busy starting or exiting since there is scope for confusion.
+ * We don't actually need to count, since sinvaladt.c maintains a
+ * freeBackends counter in the SI segment.
+ *
+ * Acquiring the lock here is almost certainly overkill, but just in
+ * case fetching an int is not atomic on your machine ...
  */
 int
 CountEmptyBackendSlots(void)
index 90b8e6afba6344a9f5428f441c6e142499ab9319..e33781a720e1cb904a6837c71a97aeb63b4d4529 100644 (file)
@@ -92,13 +92,6 @@ SIBackendInit(SISeg *segP)
        int                     index;
        ProcState  *stateP = NULL;
 
-       if (segP->freeBackends == 0)
-       {
-               /* out of procState slots */
-               MyBackendId = InvalidBackendId;
-               return 0;
-       }
-
        /* Look for a free entry in the procState array */
        for (index = 0; index < segP->lastBackend; index++)
        {
@@ -111,9 +104,18 @@ SIBackendInit(SISeg *segP)
 
        if (stateP == NULL)
        {
-               stateP = &segP->procState[segP->lastBackend];
-               Assert(stateP->nextMsgNum < 0);
-               segP->lastBackend++;
+               if (segP->lastBackend < segP->maxBackends)
+               {
+                       stateP = &segP->procState[segP->lastBackend];
+                       Assert(stateP->nextMsgNum < 0);
+                       segP->lastBackend++;
+               }
+               else
+               {
+                       /* out of procState slots */
+                       MyBackendId = InvalidBackendId;
+                       return 0;
+               }
        }
 
        MyBackendId = (stateP - &segP->procState[0]) + 1;
index 542d662ec0e2af6005f1c59807d32de7f6ca7580..0c60664b7d992a642d3222d60563b253ea25af42 100644 (file)
@@ -377,6 +377,18 @@ InitPostgres(const char *dbname, const char *username)
         */
        RelationCacheInitializePhase3();
 
+       /*
+        * Check a normal user hasn't connected to a superuser reserved slot.
+        * We can't do this till after we've read the user information, and
+        * we must do it inside a transaction since checking superuserness
+        * may require database access.  The superuser check is probably the
+        * most expensive part; don't do it until necessary.
+        */
+       if (ReservedBackends > 0 &&
+               CountEmptyBackendSlots() < ReservedBackends &&
+               !superuser())
+               elog(FATAL, "Non-superuser connection limit exceeded");
+
        /*
         * Initialize various default states that can't be set up until we've
         * selected the active user and done ReverifyMyDatabase.
@@ -397,17 +409,6 @@ InitPostgres(const char *dbname, const char *username)
        /* close the transaction we started above */
        if (!bootstrap)
                CommitTransactionCommand(true);
-
-       /*
-        * Check a normal user hasn't connected to a superuser reserved slot.
-        * Do this here since we need the user information and that only
-        * happens after we've started bringing the shared memory online. So
-        * we wait until we've registered exit handlers and potentially shut
-        * an open transaction down for an as safety conscious rejection as
-        * possible.
-        */
-       if (CountEmptyBackendSlots() < ReservedBackends && !superuser())
-               elog(ERROR, "Non-superuser connection limit exceeded");
 }
 
 /*