1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-23 03:21:12 +03:00

Add new GUC reserved_connections.

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
This commit is contained in:
Robert Haas
2023-01-20 15:36:36 -05:00
parent fe00fec1f5
commit 6e2775e4d4
10 changed files with 115 additions and 25 deletions

View File

@ -708,6 +708,37 @@ include_dir 'conf.d'
</listitem> </listitem>
</varlistentry> </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" <varlistentry id="guc-superuser-reserved-connections"
xreflabel="superuser_reserved_connections"> xreflabel="superuser_reserved_connections">
<term><varname>superuser_reserved_connections</varname> <term><varname>superuser_reserved_connections</varname>
@ -725,12 +756,16 @@ include_dir 'conf.d'
number of active concurrent connections is at least number of active concurrent connections is at least
<varname>max_connections</varname> minus <varname>max_connections</varname> minus
<varname>superuser_reserved_connections</varname>, new <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>
<para> <para>
The default value is three connections. The value must be less 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. This parameter can only be set at server start.
</para> </para>
</listitem> </listitem>

View File

@ -689,6 +689,11 @@ DROP ROLE doomed_role;
and <link linkend="sql-lock"><command>LOCK TABLE</command></link> on all and <link linkend="sql-lock"><command>LOCK TABLE</command></link> on all
relations.</entry> relations.</entry>
</row> </row>
<row>
<entry>pg_use_reserved_connections</entry>
<entry>Allow use of connection slots reserved via
<xref linkend="guc-reserved-connections"/>.</entry>
</row>
</tbody> </tbody>
</tgroup> </tgroup>
</table> </table>

View File

@ -205,14 +205,24 @@ char *ListenAddresses;
/* /*
* SuperuserReservedConnections is the number of backends reserved for * SuperuserReservedConnections is the number of backends reserved for
* superuser use. This number is taken out of the pool size given by * superuser use, and ReservedConnections is the number of backends reserved
* MaxConnections so number of backend slots available to non-superusers is * for use by roles with privileges of the pg_use_reserved_connections
* (MaxConnections - SuperuserReservedConnections). Note what this really * predefined role. These are taken out of the pool of MaxConnections backend
* means is "if there are <= SuperuserReservedConnections connections * slots, so the number of backend slots available for roles that are neither
* available, only superusers can make new connections" --- pre-existing * superuser nor have privileges of pg_use_reserved_connections is
* superuser connections don't count against the limit. * (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 SuperuserReservedConnections;
int ReservedConnections;
/* The socket(s) we're listening to. */ /* The socket(s) we're listening to. */
#define MAXLISTEN 64 #define MAXLISTEN 64
@ -908,11 +918,12 @@ PostmasterMain(int argc, char *argv[])
/* /*
* Check for invalid combinations of GUC settings. * 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, progname,
SuperuserReservedConnections, MaxConnections); SuperuserReservedConnections, ReservedConnections,
MaxConnections);
ExitPostmaster(1); ExitPostmaster(1);
} }
if (XLogArchiveMode > ARCHIVE_MODE_OFF && wal_level == WAL_LEVEL_MINIMAL) if (XLogArchiveMode > ARCHIVE_MODE_OFF && wal_level == WAL_LEVEL_MINIMAL)

View 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. * Note: this is designed on the assumption that N will generally be small.
*/ */
bool bool
HaveNFreeProcs(int n) HaveNFreeProcs(int n, int *nfree)
{ {
dlist_iter iter; dlist_iter iter;
Assert(n > 0);
Assert(nfree);
SpinLockAcquire(ProcStructLock); SpinLockAcquire(ProcStructLock);
*nfree = 0;
dlist_foreach(iter, &ProcGlobal->freeProcs) dlist_foreach(iter, &ProcGlobal->freeProcs)
{ {
n--; (*nfree)++;
if (n == 0) if (*nfree == n)
break; break;
} }
SpinLockRelease(ProcStructLock); SpinLockRelease(ProcStructLock);
return (n <= 0); return (*nfree == n);
} }
/* /*

View File

@ -719,6 +719,7 @@ InitPostgres(const char *in_dbname, Oid dboid,
bool am_superuser; bool am_superuser;
char *fullpath; char *fullpath;
char dbname[NAMEDATALEN]; char dbname[NAMEDATALEN];
int nfree = 0;
elog(DEBUG3, "InitPostgres"); elog(DEBUG3, "InitPostgres");
@ -922,16 +923,30 @@ InitPostgres(const char *in_dbname, Oid dboid,
} }
/* /*
* The last few connection slots are reserved for superusers. Replication * The last few connection slots are reserved for superusers and roles with
* connections are drawn from slots reserved with max_wal_senders and not * privileges of pg_use_reserved_connections. Replication connections are
* limited by max_connections or superuser_reserved_connections. * 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 && if (!am_superuser && !am_walsender &&
SuperuserReservedConnections > 0 && (SuperuserReservedConnections + ReservedConnections) > 0 &&
!HaveNFreeProcs(SuperuserReservedConnections)) !HaveNFreeProcs(SuperuserReservedConnections + ReservedConnections, &nfree))
ereport(FATAL, {
(errcode(ERRCODE_TOO_MANY_CONNECTIONS), if (nfree < SuperuserReservedConnections)
errmsg("remaining connection slots are reserved for superusers"))); 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. */ /* Check replication permissions needed for walsender processes. */
if (am_walsender) if (am_walsender)

View File

@ -2168,6 +2168,17 @@ struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL 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, {"min_dynamic_shared_memory", PGC_POSTMASTER, RESOURCES_MEM,
gettext_noop("Amount of dynamic shared memory reserved at startup."), gettext_noop("Amount of dynamic shared memory reserved at startup."),

View File

@ -63,6 +63,7 @@
# (change requires restart) # (change requires restart)
#port = 5432 # (change requires restart) #port = 5432 # (change requires restart)
#max_connections = 100 # (change requires restart) #max_connections = 100 # (change requires restart)
#reserved_connections = 0 # (change requires restart)
#superuser_reserved_connections = 3 # (change requires restart) #superuser_reserved_connections = 3 # (change requires restart)
#unix_socket_directories = '/tmp' # comma-separated list of directories #unix_socket_directories = '/tmp' # comma-separated list of directories
# (change requires restart) # (change requires restart)

View File

@ -89,5 +89,10 @@
rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f',
rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1',
rolpassword => '_null_', rolvaliduntil => '_null_' }, 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_' },
] ]

View File

@ -16,6 +16,7 @@
/* GUC options */ /* GUC options */
extern PGDLLIMPORT bool EnableSSL; extern PGDLLIMPORT bool EnableSSL;
extern PGDLLIMPORT int SuperuserReservedConnections; extern PGDLLIMPORT int SuperuserReservedConnections;
extern PGDLLIMPORT int ReservedConnections;
extern PGDLLIMPORT int PostPortNumber; extern PGDLLIMPORT int PostPortNumber;
extern PGDLLIMPORT int Unix_socket_permissions; extern PGDLLIMPORT int Unix_socket_permissions;
extern PGDLLIMPORT char *Unix_socket_group; extern PGDLLIMPORT char *Unix_socket_group;

View File

@ -445,7 +445,7 @@ extern void InitAuxiliaryProcess(void);
extern void SetStartupBufferPinWaitBufId(int bufid); extern void SetStartupBufferPinWaitBufId(int bufid);
extern int GetStartupBufferPinWaitBufId(void); extern int GetStartupBufferPinWaitBufId(void);
extern bool HaveNFreeProcs(int n); extern bool HaveNFreeProcs(int n, int *nfree);
extern void ProcReleaseLocks(bool isCommit); extern void ProcReleaseLocks(bool isCommit);
extern ProcWaitStatus ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable); extern ProcWaitStatus ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable);