1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-30 11:03:19 +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

@ -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)

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.
*/
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);
}
/*

View 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)

View 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."),

View 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)

View File

@ -89,5 +89,10 @@
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_' },
]

View 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;

View 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);