From 27fc7000943bfddc4a4df3ca0fce6b02fb6a94b7 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Tue, 6 Jul 2021 11:34:08 +0000
Subject: [PATCH v3] Use a WaitLatch for Pre and Post Auth Delay

Instead of using pg_usleep() for waiting PostAuthDelay and
PreAuthDelay, use a WaitLatch. This has the advantage that
we will realize if the postmaster has been killed since the
last time we decided to sleep.

For PostAuthDelay, don't use WL_LATCH_SET. Because MyLatch, on
which WaitLatch going to wait, could have been set in
SwitchToSharedLatch() by InitProcess(). For PreAuthDelay, although
there's no problem to use WL_LATCH_SET as MyLatch still points to local
latch in BackendInitialize(), let's not use it as the MyLatch may
point to shared latch later.
---
 doc/src/sgml/monitoring.sgml            | 10 ++++++++++
 src/backend/postmaster/autovacuum.c     | 24 ++++++++++++++++++++++--
 src/backend/postmaster/bgworker.c       | 11 ++++++++++-
 src/backend/postmaster/postmaster.c     | 11 ++++++++++-
 src/backend/utils/activity/wait_event.c |  6 ++++++
 src/backend/utils/init/postinit.c       | 22 ++++++++++++++++++++--
 src/include/utils/wait_event.h          |  2 ++
 7 files changed, 80 insertions(+), 6 deletions(-)

diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 643e1ad49f..3f04704832 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -2228,6 +2228,16 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       <entry>Waiting due to a call to <function>pg_sleep</function> or
        a sibling function.</entry>
      </row>
+     <row>
+      <entry><literal>PostAuthDelay</literal></entry>
+      <entry>Waiting on connection startup after authentication to allow attach
+      from a debugger.</entry>
+     </row>
+     <row>
+      <entry><literal>PreAuthDelay</literal></entry>
+      <entry>Waiting on connection startup before authentication to allow
+      attach from a debugger.</entry>
+     </row>
      <row>
       <entry><literal>RecoveryApplyDelay</literal></entry>
       <entry>Waiting to apply WAL during recovery because of a delay
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 912ef9cb54..336dde07b2 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -446,8 +446,18 @@ AutoVacLauncherMain(int argc, char *argv[])
 	ereport(DEBUG1,
 			(errmsg_internal("autovacuum launcher started")));
 
+	/* Apply PostAuthDelay */
 	if (PostAuthDelay)
-		pg_usleep(PostAuthDelay * 1000000L);
+	{
+		/*
+		 * PostAuthDelay will not get applied, if WL_LATCH_SET is used. This
+		 * is because the latch could have been set initially.
+		 */
+		(void) WaitLatch(MyLatch,
+						 WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+						 PostAuthDelay * 1000L,
+						 WAIT_EVENT_POST_AUTH_DELAY);
+	}
 
 	SetProcessingMode(InitProcessing);
 
@@ -1706,8 +1716,18 @@ AutoVacWorkerMain(int argc, char *argv[])
 		ereport(DEBUG1,
 				(errmsg_internal("autovacuum: processing database \"%s\"", dbname)));
 
+		/* Apply PostAuthDelay */
 		if (PostAuthDelay)
-			pg_usleep(PostAuthDelay * 1000000L);
+		{
+			/*
+			 * PostAuthDelay will not get applied, if WL_LATCH_SET is used.
+			 * This is because the latch could have been set initially.
+			 */
+			(void) WaitLatch(MyLatch,
+							 WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+							 PostAuthDelay * 1000L,
+							 WAIT_EVENT_POST_AUTH_DELAY);
+		}
 
 		/* And do an appropriate amount of work */
 		recentXid = ReadNextTransactionId();
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index c40410d73e..9c97a5d1f6 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -763,7 +763,16 @@ StartBackgroundWorker(void)
 
 	/* Apply PostAuthDelay */
 	if (PostAuthDelay > 0)
-		pg_usleep(PostAuthDelay * 1000000L);
+	{
+		/*
+		 * PostAuthDelay will not get applied, if WL_LATCH_SET is used. This
+		 * is because the latch could have been set initially.
+		 */
+		(void) WaitLatch(MyLatch,
+						 WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+						 PostAuthDelay * 1000L,
+						 WAIT_EVENT_POST_AUTH_DELAY);
+	}
 
 	/*
 	 * Set up signal handlers.
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 5a050898fe..f880eaf682 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -4332,7 +4332,16 @@ BackendInitialize(Port *port)
 	 * is not honored until after authentication.)
 	 */
 	if (PreAuthDelay > 0)
-		pg_usleep(PreAuthDelay * 1000000L);
+	{
+		/*
+		 * Do not use WL_LATCH_SET during backend initialization because the
+		 * MyLatch may point to shared latch later.
+		 */
+		(void) WaitLatch(MyLatch,
+						 WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+						 PreAuthDelay * 1000L,
+						 WAIT_EVENT_PRE_AUTH_DELAY);
+	}
 
 	/* This flag will remain set until InitPostgres finishes authentication */
 	ClientAuthInProgress = true;	/* limit visibility of log messages */
diff --git a/src/backend/utils/activity/wait_event.c b/src/backend/utils/activity/wait_event.c
index ef7e6bfb77..9b2835bef8 100644
--- a/src/backend/utils/activity/wait_event.c
+++ b/src/backend/utils/activity/wait_event.c
@@ -476,6 +476,12 @@ pgstat_get_wait_timeout(WaitEventTimeout w)
 		case WAIT_EVENT_PG_SLEEP:
 			event_name = "PgSleep";
 			break;
+		case WAIT_EVENT_POST_AUTH_DELAY:
+			event_name = "PostAuthDelay";
+			break;
+		case WAIT_EVENT_PRE_AUTH_DELAY:
+			event_name = "PreAuthDelay";
+			break;
 		case WAIT_EVENT_RECOVERY_APPLY_DELAY:
 			event_name = "RecoveryApplyDelay";
 			break;
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 51d1bbef30..c5491f635f 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -849,7 +849,16 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 
 		/* Apply PostAuthDelay as soon as we've read all options */
 		if (PostAuthDelay > 0)
-			pg_usleep(PostAuthDelay * 1000000L);
+		{
+			/*
+			 * PostAuthDelay will not get applied, if WL_LATCH_SET is used.
+			 * This is because the latch could have been set initially.
+			 */
+			(void) WaitLatch(MyLatch,
+							 WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+							 PostAuthDelay * 1000L,
+							 WAIT_EVENT_POST_AUTH_DELAY);
+		}
 
 		/* initialize client encoding */
 		InitializeClientEncoding();
@@ -1055,7 +1064,16 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 
 	/* Apply PostAuthDelay as soon as we've read all options */
 	if (PostAuthDelay > 0)
-		pg_usleep(PostAuthDelay * 1000000L);
+	{
+		/*
+		 * PostAuthDelay will not get applied, if WL_LATCH_SET is used. This
+		 * is because the latch could have been set initially.
+		 */
+		(void) WaitLatch(MyLatch,
+						 WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
+						 PostAuthDelay * 1000L,
+						 WAIT_EVENT_POST_AUTH_DELAY);
+	}
 
 	/*
 	 * Initialize various default states that can't be set up until we've
diff --git a/src/include/utils/wait_event.h b/src/include/utils/wait_event.h
index 6007827b44..1154f537b3 100644
--- a/src/include/utils/wait_event.h
+++ b/src/include/utils/wait_event.h
@@ -138,6 +138,8 @@ typedef enum
 {
 	WAIT_EVENT_BASE_BACKUP_THROTTLE = PG_WAIT_TIMEOUT,
 	WAIT_EVENT_PG_SLEEP,
+	WAIT_EVENT_POST_AUTH_DELAY,
+	WAIT_EVENT_PRE_AUTH_DELAY,
 	WAIT_EVENT_RECOVERY_APPLY_DELAY,
 	WAIT_EVENT_RECOVERY_RETRIEVE_RETRY_INTERVAL,
 	WAIT_EVENT_VACUUM_DELAY,
-- 
2.25.1

