Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit c76db55

Browse files
committed
Split pgstat_bestart() into three different routines
pgstat_bestart(), used post-authentication to set up a backend entry in the PgBackendStatus array, so as its data becomes visible in pg_stat_activity and related catalogs, has its logic divided into three routines with this commit, called in order at different steps of the backend initialization: * pgstat_bestart_initial() sets up the backend entry with a minimal amount of information, reporting it with a new BackendState called STATE_STARTING while waiting for backend initialization and client authentication to complete. The main benefit that this offers is observability, so as it is possible to monitor the backend activity during authentication. This step happens earlier than in the logic prior to this commit. pgstat_beinit() happens earlier as well, before authentication. * pgstat_bestart_security() reports the SSL/GSS status of the connection, once authentication completes. Auxiliary processes, for example, do not need to call this step, hence it is optional. This step is called after performing authentication, same as previously. * pgstat_bestart_final() reports the user and database IDs, takes the entry out of STATE_STARTING, and reports its application_name. This is called as the last step of the three, once authentication completes. An injection point is added, with a test checking that the "starting" phase of a backend entry is visible in pg_stat_activity. Some follow-up patches are planned to take advantage of this refactoring with more information provided in backend entries during authentication (LDAP hanging was a problem for the author, initially). Author: Jacob Champion <jacob.champion@enterprisedb.com> Reviewed-by: Michael Paquier <michael@paquier.xyz> Reviewed-by: Andres Freund <andres@anarazel.de> Discussion: https://postgr.es/m/CAOYmi+=60deN20WDyCoHCiecgivJxr=98s7s7-C8SkXwrCfHXg@mail.gmail.com
1 parent 40d3f82 commit c76db55

File tree

9 files changed

+272
-84
lines changed

9 files changed

+272
-84
lines changed

doc/src/sgml/monitoring.sgml

+6
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,12 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
899899
Current overall state of this backend.
900900
Possible values are:
901901
<itemizedlist>
902+
<listitem>
903+
<para>
904+
<literal>starting</literal>: The backend is in initial startup. Client
905+
authentication is performed during this phase.
906+
</para>
907+
</listitem>
902908
<listitem>
903909
<para>
904910
<literal>active</literal>: The backend is executing a query.

src/backend/postmaster/auxprocess.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ AuxiliaryProcessMainCommon(void)
7878

7979
/* Initialize backend status information */
8080
pgstat_beinit();
81-
pgstat_bestart();
81+
pgstat_bestart_initial();
82+
pgstat_bestart_final();
8283

8384
/* register a before-shutdown callback for LWLock cleanup */
8485
before_shmem_exit(ShutdownAuxiliaryProcess, 0);

src/backend/utils/activity/backend_status.c

+138-72
Original file line numberDiff line numberDiff line change
@@ -255,29 +255,22 @@ pgstat_beinit(void)
255255

256256

257257
/* ----------
258-
* pgstat_bestart() -
258+
* pgstat_bestart_initial() -
259259
*
260-
* Initialize this backend's entry in the PgBackendStatus array.
261-
* Called from InitPostgres.
260+
* Initialize this backend's entry in the PgBackendStatus array. Called
261+
* from InitPostgres and AuxiliaryProcessMain.
262262
*
263-
* Apart from auxiliary processes, MyDatabaseId, session userid, and
264-
* application_name must already be set (hence, this cannot be combined
265-
* with pgstat_beinit). Note also that we must be inside a transaction
266-
* if this isn't an aux process, as we may need to do encoding conversion
267-
* on some strings.
268-
*----------
263+
* Clears out a new pgstat entry, initializing it to suitable defaults and
264+
* reporting STATE_STARTING. Backends should continue filling in any
265+
* transport security details as needed with pgstat_bestart_security(), and
266+
* must finally exit STATE_STARTING by calling pgstat_bestart_final().
267+
* ----------
269268
*/
270269
void
271-
pgstat_bestart(void)
270+
pgstat_bestart_initial(void)
272271
{
273272
volatile PgBackendStatus *vbeentry = MyBEEntry;
274273
PgBackendStatus lbeentry;
275-
#ifdef USE_SSL
276-
PgBackendSSLStatus lsslstatus;
277-
#endif
278-
#ifdef ENABLE_GSS
279-
PgBackendGSSStatus lgssstatus;
280-
#endif
281274

282275
/* pgstats state must be initialized from pgstat_beinit() */
283276
Assert(vbeentry != NULL);
@@ -297,14 +290,6 @@ pgstat_bestart(void)
297290
unvolatize(PgBackendStatus *, vbeentry),
298291
sizeof(PgBackendStatus));
299292

300-
/* These structs can just start from zeroes each time, though */
301-
#ifdef USE_SSL
302-
memset(&lsslstatus, 0, sizeof(lsslstatus));
303-
#endif
304-
#ifdef ENABLE_GSS
305-
memset(&lgssstatus, 0, sizeof(lgssstatus));
306-
#endif
307-
308293
/*
309294
* Now fill in all the fields of lbeentry, except for strings that are
310295
* out-of-line data. Those have to be handled separately, below.
@@ -315,15 +300,8 @@ pgstat_bestart(void)
315300
lbeentry.st_activity_start_timestamp = 0;
316301
lbeentry.st_state_start_timestamp = 0;
317302
lbeentry.st_xact_start_timestamp = 0;
318-
lbeentry.st_databaseid = MyDatabaseId;
319-
320-
/* We have userid for client-backends, wal-sender and bgworker processes */
321-
if (lbeentry.st_backendType == B_BACKEND
322-
|| lbeentry.st_backendType == B_WAL_SENDER
323-
|| lbeentry.st_backendType == B_BG_WORKER)
324-
lbeentry.st_userid = GetSessionUserId();
325-
else
326-
lbeentry.st_userid = InvalidOid;
303+
lbeentry.st_databaseid = InvalidOid;
304+
lbeentry.st_userid = InvalidOid;
327305

328306
/*
329307
* We may not have a MyProcPort (eg, if this is the autovacuum process).
@@ -336,46 +314,10 @@ pgstat_bestart(void)
336314
else
337315
MemSet(&lbeentry.st_clientaddr, 0, sizeof(lbeentry.st_clientaddr));
338316

339-
#ifdef USE_SSL
340-
if (MyProcPort && MyProcPort->ssl_in_use)
341-
{
342-
lbeentry.st_ssl = true;
343-
lsslstatus.ssl_bits = be_tls_get_cipher_bits(MyProcPort);
344-
strlcpy(lsslstatus.ssl_version, be_tls_get_version(MyProcPort), NAMEDATALEN);
345-
strlcpy(lsslstatus.ssl_cipher, be_tls_get_cipher(MyProcPort), NAMEDATALEN);
346-
be_tls_get_peer_subject_name(MyProcPort, lsslstatus.ssl_client_dn, NAMEDATALEN);
347-
be_tls_get_peer_serial(MyProcPort, lsslstatus.ssl_client_serial, NAMEDATALEN);
348-
be_tls_get_peer_issuer_name(MyProcPort, lsslstatus.ssl_issuer_dn, NAMEDATALEN);
349-
}
350-
else
351-
{
352-
lbeentry.st_ssl = false;
353-
}
354-
#else
355317
lbeentry.st_ssl = false;
356-
#endif
357-
358-
#ifdef ENABLE_GSS
359-
if (MyProcPort && MyProcPort->gss != NULL)
360-
{
361-
const char *princ = be_gssapi_get_princ(MyProcPort);
362-
363-
lbeentry.st_gss = true;
364-
lgssstatus.gss_auth = be_gssapi_get_auth(MyProcPort);
365-
lgssstatus.gss_enc = be_gssapi_get_enc(MyProcPort);
366-
lgssstatus.gss_delegation = be_gssapi_get_delegation(MyProcPort);
367-
if (princ)
368-
strlcpy(lgssstatus.gss_princ, princ, NAMEDATALEN);
369-
}
370-
else
371-
{
372-
lbeentry.st_gss = false;
373-
}
374-
#else
375318
lbeentry.st_gss = false;
376-
#endif
377319

378-
lbeentry.st_state = STATE_UNDEFINED;
320+
lbeentry.st_state = STATE_STARTING;
379321
lbeentry.st_progress_command = PROGRESS_COMMAND_INVALID;
380322
lbeentry.st_progress_command_target = InvalidOid;
381323
lbeentry.st_query_id = UINT64CONST(0);
@@ -417,14 +359,138 @@ pgstat_bestart(void)
417359
lbeentry.st_clienthostname[NAMEDATALEN - 1] = '\0';
418360
lbeentry.st_activity_raw[pgstat_track_activity_query_size - 1] = '\0';
419361

362+
/* These structs can just start from zeroes each time */
420363
#ifdef USE_SSL
421-
memcpy(lbeentry.st_sslstatus, &lsslstatus, sizeof(PgBackendSSLStatus));
364+
memset(lbeentry.st_sslstatus, 0, sizeof(PgBackendSSLStatus));
422365
#endif
423366
#ifdef ENABLE_GSS
424-
memcpy(lbeentry.st_gssstatus, &lgssstatus, sizeof(PgBackendGSSStatus));
367+
memset(lbeentry.st_gssstatus, 0, sizeof(PgBackendGSSStatus));
425368
#endif
426369

427370
PGSTAT_END_WRITE_ACTIVITY(vbeentry);
371+
}
372+
373+
/* ----------
374+
* pgstat_bestart_security() -
375+
*
376+
* Fill in SSL and GSS information for the pgstat entry. This is the second
377+
* optional step taken when filling a backend's entry, not required for
378+
* auxiliary processes.
379+
*
380+
* This should only be called from backends with a MyProcPort.
381+
* ----------
382+
*/
383+
void
384+
pgstat_bestart_security(void)
385+
{
386+
volatile PgBackendStatus *beentry = MyBEEntry;
387+
bool ssl = false;
388+
bool gss = false;
389+
#ifdef USE_SSL
390+
PgBackendSSLStatus lsslstatus;
391+
PgBackendSSLStatus *st_sslstatus;
392+
#endif
393+
#ifdef ENABLE_GSS
394+
PgBackendGSSStatus lgssstatus;
395+
PgBackendGSSStatus *st_gssstatus;
396+
#endif
397+
398+
/* pgstats state must be initialized from pgstat_beinit() */
399+
Assert(beentry != NULL);
400+
Assert(MyProcPort); /* otherwise there's no point */
401+
402+
#ifdef USE_SSL
403+
st_sslstatus = beentry->st_sslstatus;
404+
memset(&lsslstatus, 0, sizeof(lsslstatus));
405+
406+
if (MyProcPort->ssl_in_use)
407+
{
408+
ssl = true;
409+
lsslstatus.ssl_bits = be_tls_get_cipher_bits(MyProcPort);
410+
strlcpy(lsslstatus.ssl_version, be_tls_get_version(MyProcPort), NAMEDATALEN);
411+
strlcpy(lsslstatus.ssl_cipher, be_tls_get_cipher(MyProcPort), NAMEDATALEN);
412+
be_tls_get_peer_subject_name(MyProcPort, lsslstatus.ssl_client_dn, NAMEDATALEN);
413+
be_tls_get_peer_serial(MyProcPort, lsslstatus.ssl_client_serial, NAMEDATALEN);
414+
be_tls_get_peer_issuer_name(MyProcPort, lsslstatus.ssl_issuer_dn, NAMEDATALEN);
415+
}
416+
#endif
417+
418+
#ifdef ENABLE_GSS
419+
st_gssstatus = beentry->st_gssstatus;
420+
memset(&lgssstatus, 0, sizeof(lgssstatus));
421+
422+
if (MyProcPort->gss != NULL)
423+
{
424+
const char *princ = be_gssapi_get_princ(MyProcPort);
425+
426+
gss = true;
427+
lgssstatus.gss_auth = be_gssapi_get_auth(MyProcPort);
428+
lgssstatus.gss_enc = be_gssapi_get_enc(MyProcPort);
429+
lgssstatus.gss_delegation = be_gssapi_get_delegation(MyProcPort);
430+
if (princ)
431+
strlcpy(lgssstatus.gss_princ, princ, NAMEDATALEN);
432+
}
433+
#endif
434+
435+
/*
436+
* Update my status entry, following the protocol of bumping
437+
* st_changecount before and after. We use a volatile pointer here to
438+
* ensure the compiler doesn't try to get cute.
439+
*/
440+
PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
441+
442+
beentry->st_ssl = ssl;
443+
beentry->st_gss = gss;
444+
445+
#ifdef USE_SSL
446+
memcpy(st_sslstatus, &lsslstatus, sizeof(PgBackendSSLStatus));
447+
#endif
448+
#ifdef ENABLE_GSS
449+
memcpy(st_gssstatus, &lgssstatus, sizeof(PgBackendGSSStatus));
450+
#endif
451+
452+
PGSTAT_END_WRITE_ACTIVITY(beentry);
453+
}
454+
455+
/* ----------
456+
* pgstat_bestart_final() -
457+
*
458+
* Finalizes the state of this backend's entry by filling in the user and
459+
* database IDs, clearing STATE_STARTING, and reporting the application_name.
460+
*
461+
* We must be inside a transaction if this is not an auxiliary process, as
462+
* we may need to do encoding conversion.
463+
* ----------
464+
*/
465+
void
466+
pgstat_bestart_final(void)
467+
{
468+
volatile PgBackendStatus *beentry = MyBEEntry;
469+
Oid userid;
470+
471+
/* pgstats state must be initialized from pgstat_beinit() */
472+
Assert(beentry != NULL);
473+
474+
/* We have userid for client-backends, wal-sender and bgworker processes */
475+
if (MyBackendType == B_BACKEND
476+
|| MyBackendType == B_WAL_SENDER
477+
|| MyBackendType == B_BG_WORKER)
478+
userid = GetSessionUserId();
479+
else
480+
userid = InvalidOid;
481+
482+
/*
483+
* Update my status entry, following the protocol of bumping
484+
* st_changecount before and after. We use a volatile pointer here to
485+
* ensure the compiler doesn't try to get cute.
486+
*/
487+
PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
488+
489+
beentry->st_databaseid = MyDatabaseId;
490+
beentry->st_userid = userid;
491+
beentry->st_state = STATE_UNDEFINED;
492+
493+
PGSTAT_END_WRITE_ACTIVITY(beentry);
428494

429495
/* Create the backend statistics entry */
430496
if (pgstat_tracks_backend_bktype(MyBackendType))

src/backend/utils/adt/pgstatfuncs.c

+3
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,9 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
393393

394394
switch (beentry->st_state)
395395
{
396+
case STATE_STARTING:
397+
values[4] = CStringGetTextDatum("starting");
398+
break;
396399
case STATE_IDLE:
397400
values[4] = CStringGetTextDatum("idle");
398401
break;

src/backend/utils/init/postinit.c

+30-10
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
#include "utils/builtins.h"
6060
#include "utils/fmgroids.h"
6161
#include "utils/guc_hooks.h"
62+
#include "utils/injection_point.h"
6263
#include "utils/memutils.h"
6364
#include "utils/pg_locale.h"
6465
#include "utils/portal.h"
@@ -718,6 +719,20 @@ InitPostgres(const char *in_dbname, Oid dboid,
718719
*/
719720
InitProcessPhase2();
720721

722+
/* Initialize status reporting */
723+
pgstat_beinit();
724+
725+
/*
726+
* And initialize an entry in the PgBackendStatus array. That way, if
727+
* LWLocks or third-party authentication should happen to hang, it is
728+
* possible to retrieve some information about what is going on.
729+
*/
730+
if (!bootstrap)
731+
{
732+
pgstat_bestart_initial();
733+
INJECTION_POINT("init-pre-auth");
734+
}
735+
721736
/*
722737
* Initialize my entry in the shared-invalidation manager's array of
723738
* per-backend data.
@@ -786,9 +801,6 @@ InitPostgres(const char *in_dbname, Oid dboid,
786801
/* Initialize portal manager */
787802
EnablePortalManager();
788803

789-
/* Initialize status reporting */
790-
pgstat_beinit();
791-
792804
/*
793805
* Load relcache entries for the shared system catalogs. This must create
794806
* at least entries for pg_database and catalogs used for authentication.
@@ -809,8 +821,8 @@ InitPostgres(const char *in_dbname, Oid dboid,
809821
/* The autovacuum launcher is done here */
810822
if (AmAutoVacuumLauncherProcess())
811823
{
812-
/* report this backend in the PgBackendStatus array */
813-
pgstat_bestart();
824+
/* fill in the remainder of this entry in the PgBackendStatus array */
825+
pgstat_bestart_final();
814826

815827
return;
816828
}
@@ -884,6 +896,14 @@ InitPostgres(const char *in_dbname, Oid dboid,
884896
am_superuser = superuser();
885897
}
886898

899+
/* Report any SSL/GSS details for the session. */
900+
if (MyProcPort != NULL)
901+
{
902+
Assert(!bootstrap);
903+
904+
pgstat_bestart_security();
905+
}
906+
887907
/*
888908
* Binary upgrades only allowed super-user connections
889909
*/
@@ -953,8 +973,8 @@ InitPostgres(const char *in_dbname, Oid dboid,
953973
/* initialize client encoding */
954974
InitializeClientEncoding();
955975

956-
/* report this backend in the PgBackendStatus array */
957-
pgstat_bestart();
976+
/* fill in the remainder of this entry in the PgBackendStatus array */
977+
pgstat_bestart_final();
958978

959979
/* close the transaction we started above */
960980
CommitTransactionCommand();
@@ -997,7 +1017,7 @@ InitPostgres(const char *in_dbname, Oid dboid,
9971017
*/
9981018
if (!bootstrap)
9991019
{
1000-
pgstat_bestart();
1020+
pgstat_bestart_final();
10011021
CommitTransactionCommand();
10021022
}
10031023
return;
@@ -1197,9 +1217,9 @@ InitPostgres(const char *in_dbname, Oid dboid,
11971217
if ((flags & INIT_PG_LOAD_SESSION_LIBS) != 0)
11981218
process_session_preload_libraries();
11991219

1200-
/* report this backend in the PgBackendStatus array */
1220+
/* fill in the remainder of this entry in the PgBackendStatus array */
12011221
if (!bootstrap)
1202-
pgstat_bestart();
1222+
pgstat_bestart_final();
12031223

12041224
/* close the transaction we started above */
12051225
if (!bootstrap)

0 commit comments

Comments
 (0)