Skip to content

Commit 6e2775e

Browse files
committed
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
1 parent fe00fec commit 6e2775e

File tree

10 files changed

+115
-25
lines changed

10 files changed

+115
-25
lines changed

doc/src/sgml/config.sgml

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,37 @@ include_dir 'conf.d'
708708
</listitem>
709709
</varlistentry>
710710

711+
<varlistentry id="guc-reserved-connections" xreflabel="reserved_connections">
712+
<term><varname>reserved_connections</varname> (<type>integer</type>)
713+
<indexterm>
714+
<primary><varname>reserved_connections</varname> configuration parameter</primary>
715+
</indexterm>
716+
</term>
717+
<listitem>
718+
<para>
719+
Determines the number of connection <quote>slots</quote> that are
720+
reserved for connections by roles with privileges of the
721+
<link linkend="predefined-roles-table"><literal>pg_used_reserved_connections</literal></link>
722+
role. Whenever the number of free connection slots is greater than
723+
<xref linkend="guc-superuser-reserved-connections"/> but less than or
724+
equal to the sum of <varname>superuser_reserved_connections</varname>
725+
and <varname>reserved_connections</varname>, new connections will be
726+
accepted only for superusers and roles with privileges of
727+
<literal>pg_use_reserved_connections</literal>. If
728+
<varname>superuser_reserved_connections</varname> or fewer connection
729+
slots are available, new connections will be accepted only for
730+
superusers.
731+
</para>
732+
733+
<para>
734+
The default value is zero connections. The value must be less than
735+
<varname>max_connections</varname> minus
736+
<varname>superuser_reserved_connections</varname>. This parameter can
737+
only be set at server start.
738+
</para>
739+
</listitem>
740+
</varlistentry>
741+
711742
<varlistentry id="guc-superuser-reserved-connections"
712743
xreflabel="superuser_reserved_connections">
713744
<term><varname>superuser_reserved_connections</varname>
@@ -725,12 +756,16 @@ include_dir 'conf.d'
725756
number of active concurrent connections is at least
726757
<varname>max_connections</varname> minus
727758
<varname>superuser_reserved_connections</varname>, new
728-
connections will be accepted only for superusers.
759+
connections will be accepted only for superusers. The connection slots
760+
reserved by this parameter are intended as final reserve for emergency
761+
use after the slots reserved by
762+
<xref linkend="guc-reserved-connections"/> have been exhausted.
729763
</para>
730764

731765
<para>
732766
The default value is three connections. The value must be less
733-
than <varname>max_connections</varname>.
767+
than <varname>max_connections</varname> minus
768+
<varname>reserved_connections</varname>.
734769
This parameter can only be set at server start.
735770
</para>
736771
</listitem>

doc/src/sgml/user-manag.sgml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,11 @@ DROP ROLE doomed_role;
689689
and <link linkend="sql-lock"><command>LOCK TABLE</command></link> on all
690690
relations.</entry>
691691
</row>
692+
<row>
693+
<entry>pg_use_reserved_connections</entry>
694+
<entry>Allow use of connection slots reserved via
695+
<xref linkend="guc-reserved-connections"/>.</entry>
696+
</row>
692697
</tbody>
693698
</tgroup>
694699
</table>

src/backend/postmaster/postmaster.c

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -205,14 +205,24 @@ char *ListenAddresses;
205205

206206
/*
207207
* SuperuserReservedConnections is the number of backends reserved for
208-
* superuser use. This number is taken out of the pool size given by
209-
* MaxConnections so number of backend slots available to non-superusers is
210-
* (MaxConnections - SuperuserReservedConnections). Note what this really
211-
* means is "if there are <= SuperuserReservedConnections connections
212-
* available, only superusers can make new connections" --- pre-existing
213-
* superuser connections don't count against the limit.
208+
* superuser use, and ReservedConnections is the number of backends reserved
209+
* for use by roles with privileges of the pg_use_reserved_connections
210+
* predefined role. These are taken out of the pool of MaxConnections backend
211+
* slots, so the number of backend slots available for roles that are neither
212+
* superuser nor have privileges of pg_use_reserved_connections is
213+
* (MaxConnections - SuperuserReservedConnections - ReservedConnections).
214+
*
215+
* If the number of remaining slots is less than or equal to
216+
* SuperuserReservedConnections, only superusers can make new connections. If
217+
* the number of remaining slots is greater than SuperuserReservedConnections
218+
* but less than or equal to
219+
* (SuperuserReservedConnections + ReservedConnections), only superusers and
220+
* roles with privileges of pg_use_reserved_connections can make new
221+
* connections. Note that pre-existing superuser and
222+
* pg_use_reserved_connections connections don't count against the limits.
214223
*/
215224
int SuperuserReservedConnections;
225+
int ReservedConnections;
216226

217227
/* The socket(s) we're listening to. */
218228
#define MAXLISTEN 64
@@ -908,11 +918,12 @@ PostmasterMain(int argc, char *argv[])
908918
/*
909919
* Check for invalid combinations of GUC settings.
910920
*/
911-
if (SuperuserReservedConnections >= MaxConnections)
921+
if (SuperuserReservedConnections + ReservedConnections >= MaxConnections)
912922
{
913-
write_stderr("%s: superuser_reserved_connections (%d) must be less than max_connections (%d)\n",
923+
write_stderr("%s: superuser_reserved_connections (%d) plus reserved_connections (%d) must be less than max_connections (%d)\n",
914924
progname,
915-
SuperuserReservedConnections, MaxConnections);
925+
SuperuserReservedConnections, ReservedConnections,
926+
MaxConnections);
916927
ExitPostmaster(1);
917928
}
918929
if (XLogArchiveMode > ARCHIVE_MODE_OFF && wal_level == WAL_LEVEL_MINIMAL)

src/backend/storage/lmgr/proc.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -645,27 +645,33 @@ GetStartupBufferPinWaitBufId(void)
645645
}
646646

647647
/*
648-
* Check whether there are at least N free PGPROC objects.
648+
* Check whether there are at least N free PGPROC objects. If false is
649+
* returned, *nfree will be set to the number of free PGPROC objects.
650+
* Otherwise, *nfree will be set to n.
649651
*
650652
* Note: this is designed on the assumption that N will generally be small.
651653
*/
652654
bool
653-
HaveNFreeProcs(int n)
655+
HaveNFreeProcs(int n, int *nfree)
654656
{
655657
dlist_iter iter;
656658

659+
Assert(n > 0);
660+
Assert(nfree);
661+
657662
SpinLockAcquire(ProcStructLock);
658663

664+
*nfree = 0;
659665
dlist_foreach(iter, &ProcGlobal->freeProcs)
660666
{
661-
n--;
662-
if (n == 0)
667+
(*nfree)++;
668+
if (*nfree == n)
663669
break;
664670
}
665671

666672
SpinLockRelease(ProcStructLock);
667673

668-
return (n <= 0);
674+
return (*nfree == n);
669675
}
670676

671677
/*

src/backend/utils/init/postinit.c

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,7 @@ InitPostgres(const char *in_dbname, Oid dboid,
719719
bool am_superuser;
720720
char *fullpath;
721721
char dbname[NAMEDATALEN];
722+
int nfree = 0;
722723

723724
elog(DEBUG3, "InitPostgres");
724725

@@ -922,16 +923,30 @@ InitPostgres(const char *in_dbname, Oid dboid,
922923
}
923924

924925
/*
925-
* The last few connection slots are reserved for superusers. Replication
926-
* connections are drawn from slots reserved with max_wal_senders and not
927-
* limited by max_connections or superuser_reserved_connections.
926+
* The last few connection slots are reserved for superusers and roles with
927+
* privileges of pg_use_reserved_connections. Replication connections are
928+
* drawn from slots reserved with max_wal_senders and are not limited by
929+
* max_connections, superuser_reserved_connections, or
930+
* reserved_connections.
931+
*
932+
* Note: At this point, the new backend has already claimed a proc struct,
933+
* so we must check whether the number of free slots is strictly less than
934+
* the reserved connection limits.
928935
*/
929936
if (!am_superuser && !am_walsender &&
930-
SuperuserReservedConnections > 0 &&
931-
!HaveNFreeProcs(SuperuserReservedConnections))
932-
ereport(FATAL,
933-
(errcode(ERRCODE_TOO_MANY_CONNECTIONS),
934-
errmsg("remaining connection slots are reserved for superusers")));
937+
(SuperuserReservedConnections + ReservedConnections) > 0 &&
938+
!HaveNFreeProcs(SuperuserReservedConnections + ReservedConnections, &nfree))
939+
{
940+
if (nfree < SuperuserReservedConnections)
941+
ereport(FATAL,
942+
(errcode(ERRCODE_TOO_MANY_CONNECTIONS),
943+
errmsg("remaining connection slots are reserved for superusers")));
944+
945+
if (!has_privs_of_role(GetUserId(), ROLE_PG_USE_RESERVED_CONNECTIONS))
946+
ereport(FATAL,
947+
(errcode(ERRCODE_TOO_MANY_CONNECTIONS),
948+
errmsg("remaining connection slots are reserved for roles with privileges of pg_use_reserved_connections")));
949+
}
935950

936951
/* Check replication permissions needed for walsender processes. */
937952
if (am_walsender)

src/backend/utils/misc/guc_tables.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2168,6 +2168,17 @@ struct config_int ConfigureNamesInt[] =
21682168
NULL, NULL, NULL
21692169
},
21702170

2171+
{
2172+
{"reserved_connections", PGC_POSTMASTER, CONN_AUTH_SETTINGS,
2173+
gettext_noop("Sets the number of connection slots reserved for roles "
2174+
"with privileges of pg_use_reserved_connections."),
2175+
NULL
2176+
},
2177+
&ReservedConnections,
2178+
0, 0, MAX_BACKENDS,
2179+
NULL, NULL, NULL
2180+
},
2181+
21712182
{
21722183
{"min_dynamic_shared_memory", PGC_POSTMASTER, RESOURCES_MEM,
21732184
gettext_noop("Amount of dynamic shared memory reserved at startup."),

src/backend/utils/misc/postgresql.conf.sample

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
# (change requires restart)
6464
#port = 5432 # (change requires restart)
6565
#max_connections = 100 # (change requires restart)
66+
#reserved_connections = 0 # (change requires restart)
6667
#superuser_reserved_connections = 3 # (change requires restart)
6768
#unix_socket_directories = '/tmp' # comma-separated list of directories
6869
# (change requires restart)

src/include/catalog/pg_authid.dat

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,5 +89,10 @@
8989
rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f',
9090
rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1',
9191
rolpassword => '_null_', rolvaliduntil => '_null_' },
92+
{ oid => '4550', oid_symbol => 'ROLE_PG_USE_RESERVED_CONNECTIONS',
93+
rolname => 'pg_use_reserved_connections', rolsuper => 'f', rolinherit => 't',
94+
rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f',
95+
rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1',
96+
rolpassword => '_null_', rolvaliduntil => '_null_' },
9297

9398
]

src/include/postmaster/postmaster.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
/* GUC options */
1717
extern PGDLLIMPORT bool EnableSSL;
1818
extern PGDLLIMPORT int SuperuserReservedConnections;
19+
extern PGDLLIMPORT int ReservedConnections;
1920
extern PGDLLIMPORT int PostPortNumber;
2021
extern PGDLLIMPORT int Unix_socket_permissions;
2122
extern PGDLLIMPORT char *Unix_socket_group;

src/include/storage/proc.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ extern void InitAuxiliaryProcess(void);
445445
extern void SetStartupBufferPinWaitBufId(int bufid);
446446
extern int GetStartupBufferPinWaitBufId(void);
447447

448-
extern bool HaveNFreeProcs(int n);
448+
extern bool HaveNFreeProcs(int n, int *nfree);
449449
extern void ProcReleaseLocks(bool isCommit);
450450

451451
extern ProcWaitStatus ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable);

0 commit comments

Comments
 (0)