From a0308928dd3c6dbb83c43f74bb3913f9bccf8596 Mon Sep 17 00:00:00 2001
From: Jacob Champion <jacob.champion@enterprisedb.com>
Date: Fri, 8 Nov 2024 14:19:26 -0800
Subject: [PATCH v7 3/3] squash! Report external auth calls as wait events

Add a wait event around all calls to ldap_unbind().
---
 src/backend/libpq/auth.c                      | 33 ++++++++++++++-----
 .../utils/activity/wait_event_names.txt       |  1 +
 2 files changed, 25 insertions(+), 9 deletions(-)

diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index fe4cbeb8a0..df38d66f48 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -2226,6 +2226,7 @@ CheckBSDAuth(Port *port, char *user)
 #ifdef USE_LDAP
 
 static int	errdetail_for_ldap(LDAP *ldap);
+static void unbind(LDAP *ldap);
 
 /*
  * Initialize a connection to the LDAP server, including setting up
@@ -2383,7 +2384,7 @@ InitializeLDAPConnection(Port *port, LDAP **ldap)
 				(errmsg("could not set LDAP protocol version: %s",
 						ldap_err2string(r)),
 				 errdetail_for_ldap(*ldap)));
-		ldap_unbind(*ldap);
+		unbind(*ldap);
 		return STATUS_ERROR;
 	}
 
@@ -2403,7 +2404,7 @@ InitializeLDAPConnection(Port *port, LDAP **ldap)
 					(errmsg("could not start LDAP TLS session: %s",
 							ldap_err2string(r)),
 					 errdetail_for_ldap(*ldap)));
-			ldap_unbind(*ldap);
+			unbind(*ldap);
 			return STATUS_ERROR;
 		}
 	}
@@ -2547,7 +2548,7 @@ CheckLDAPAuth(Port *port)
 			{
 				ereport(LOG,
 						(errmsg("invalid character in user name for LDAP authentication")));
-				ldap_unbind(ldap);
+				unbind(ldap);
 				pfree(passwd);
 				return STATUS_ERROR;
 			}
@@ -2571,7 +2572,7 @@ CheckLDAPAuth(Port *port)
 							server_name,
 							ldap_err2string(r)),
 					 errdetail_for_ldap(ldap)));
-			ldap_unbind(ldap);
+			unbind(ldap);
 			pfree(passwd);
 			return STATUS_ERROR;
 		}
@@ -2604,7 +2605,7 @@ CheckLDAPAuth(Port *port)
 					 errdetail_for_ldap(ldap)));
 			if (search_message != NULL)
 				ldap_msgfree(search_message);
-			ldap_unbind(ldap);
+			unbind(ldap);
 			pfree(passwd);
 			pfree(filter);
 			return STATUS_ERROR;
@@ -2626,7 +2627,7 @@ CheckLDAPAuth(Port *port)
 										  count,
 										  filter, server_name, count)));
 
-			ldap_unbind(ldap);
+			unbind(ldap);
 			pfree(passwd);
 			pfree(filter);
 			ldap_msgfree(search_message);
@@ -2645,7 +2646,7 @@ CheckLDAPAuth(Port *port)
 							filter, server_name,
 							ldap_err2string(error)),
 					 errdetail_for_ldap(ldap)));
-			ldap_unbind(ldap);
+			unbind(ldap);
 			pfree(passwd);
 			pfree(filter);
 			ldap_msgfree(search_message);
@@ -2673,7 +2674,7 @@ CheckLDAPAuth(Port *port)
 				(errmsg("LDAP login failed for user \"%s\" on server \"%s\": %s",
 						fulluser, server_name, ldap_err2string(r)),
 				 errdetail_for_ldap(ldap)));
-		ldap_unbind(ldap);
+		unbind(ldap);
 		pfree(passwd);
 		pfree(fulluser);
 		return STATUS_ERROR;
@@ -2682,7 +2683,7 @@ CheckLDAPAuth(Port *port)
 	/* Save the original bind DN as the authenticated identity. */
 	set_authn_id(port, fulluser);
 
-	ldap_unbind(ldap);
+	unbind(ldap);
 	pfree(passwd);
 	pfree(fulluser);
 
@@ -2709,6 +2710,20 @@ errdetail_for_ldap(LDAP *ldap)
 	return 0;
 }
 
+/*
+ * ldap_unbind() is not a lightweight operation; it writes data to the socket
+ * and may need to tear down (LDAP) SASL mechanisms, depending on the LDAP
+ * implementation in use. This function just wraps the unbind with pgstat
+ * reporting in case something takes longer than expected.
+ */
+static void
+unbind(LDAP *ldap)
+{
+	pgstat_report_wait_start(WAIT_EVENT_LDAP_UNBIND);
+	ldap_unbind(ldap);
+	pgstat_report_wait_end();
+}
+
 #endif							/* USE_LDAP */
 
 
diff --git a/src/backend/utils/activity/wait_event_names.txt b/src/backend/utils/activity/wait_event_names.txt
index db8474bc85..00a9459ad4 100644
--- a/src/backend/utils/activity/wait_event_names.txt
+++ b/src/backend/utils/activity/wait_event_names.txt
@@ -176,6 +176,7 @@ LDAP_BIND_FOR_SEARCH	"Waiting for an LDAP bind operation to search the directory
 LDAP_HOST_LOOKUP	"Waiting for DNS lookup of LDAP servers."
 LDAP_SEARCH	"Waiting for an LDAP search operation to complete."
 LDAP_START_TLS	"Waiting for an LDAP StartTLS exchange."
+LDAP_UNBIND	"Waiting for an LDAP connection to be unbound and closed."
 PAM_ACCT_MGMT	"Waiting for the PAM service to validate the user account."
 PAM_AUTHENTICATE	"Waiting for the PAM service to authenticate the user."
 RADIUS_RECVFROM	"Waiting for a <function>recvfrom</function> call during a RADIUS transaction."
-- 
2.34.1

