From fb3dbdf986f0ab56e0a5177966bb0dec1f93444c Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Fri, 16 Dec 2005 04:03:40 +0000
Subject: [PATCH] Rethink prior patch to filter out dead backend entries from
 the pgstats file.  The original code probed the PGPROC array separately for
 each PID, which was not good for large numbers of backends: not only is the
 runtime O(N^2) but most of it is spent holding ProcArrayLock.  Instead, take
 the lock just once and copy the active PIDs into an array, then use qsort and
 bsearch so that the lookup time is more like O(N log N).

---
 src/backend/postmaster/pgstat.c     | 30 +++++++++++++++++++----
 src/backend/storage/ipc/procarray.c | 38 ++++++++++++++++++++++++++++-
 src/include/storage/procarray.h     |  3 ++-
 3 files changed, 64 insertions(+), 7 deletions(-)

diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 82cf1bc4ef..ae20a3bb71 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -13,7 +13,7 @@
  *
  *	Copyright (c) 2001-2005, PostgreSQL Global Development Group
  *
- *	$PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.112 2005/11/22 18:17:17 momjian Exp $
+ *	$PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.113 2005/12/16 04:03:40 tgl Exp $
  * ----------
  */
 #include "postgres.h"
@@ -2453,6 +2453,16 @@ pgstat_write_statsfile(void)
 	}
 }
 
+/*
+ * qsort/bsearch comparison routine for PIDs
+ *
+ * We assume PIDs are nonnegative, so there's no overflow risk
+ */
+static int
+comparePids(const void *v1, const void *v2)
+{
+	return *((const int *) v1) - *((const int *) v2);
+}
 
 /* ----------
  * pgstat_read_statsfile() -
@@ -2478,7 +2488,7 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb,
 	int			maxbackends = 0;
 	int			havebackends = 0;
 	bool		found;
-	bool		check_pids;
+	int		   *live_pids;
 	MemoryContext use_mcxt;
 	int			mcxt_flags;
 
@@ -2497,13 +2507,17 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb,
 	{
 		use_mcxt = NULL;
 		mcxt_flags = 0;
-		check_pids = false;
+		live_pids = NULL;
 	}
 	else
 	{
 		use_mcxt = TopTransactionContext;
 		mcxt_flags = HASH_CONTEXT;
-		check_pids = true;
+		live_pids = GetAllBackendPids();
+		/* Sort the PID array so we can use bsearch */
+		if (live_pids[0] > 1)
+			qsort((void *) &live_pids[1], live_pids[0], sizeof(int),
+				  comparePids);
 	}
 
 	/*
@@ -2706,7 +2720,13 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb,
 				/*
 				 * If possible, check PID to verify still running
 				 */
-				if (check_pids && !IsBackendPid(beentry->procpid))
+				if (live_pids &&
+					(live_pids[0] == 0 ||
+					 bsearch((void *) &beentry->procpid,
+							 (void *) &live_pids[1],
+							 live_pids[0],
+							 sizeof(int),
+							 comparePids) == NULL))
 				{
 					/*
 					 * Note: we could send a BETERM message to tell the
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index cafadeb905..f633080748 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -23,7 +23,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/storage/ipc/procarray.c,v 1.9 2005/12/11 21:02:18 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/storage/ipc/procarray.c,v 1.10 2005/12/16 04:03:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -732,6 +732,42 @@ IsBackendPid(int pid)
 	return (BackendPidGetProc(pid) != NULL);
 }
 
+/*
+ * GetAllBackendPids -- get an array of all current backends' PIDs
+ *
+ * The result is a palloc'd array with the number of active backends in
+ * entry [0], their PIDs in entries [1] .. [n].  The caller must bear in
+ * mind that the result may already be obsolete when returned.
+ */
+int *
+GetAllBackendPids(void)
+{
+	int		   *result;
+	int			npids;
+	ProcArrayStruct *arrayP = procArray;
+	int			index;
+
+	result = (int *) palloc((MaxBackends + 1) * sizeof(int));
+	npids = 0;
+
+	LWLockAcquire(ProcArrayLock, LW_SHARED);
+
+	for (index = 0; index < arrayP->numProcs; index++)
+	{
+		PGPROC	   *proc = arrayP->procs[index];
+
+		if (proc->pid != 0)		/* ignore dummy procs */
+			result[++npids] = proc->pid;
+	}
+
+	LWLockRelease(ProcArrayLock);
+
+	Assert(npids <= MaxBackends);
+
+	result[0] = npids;
+	return result;
+}
+
 /*
  * CountActiveBackends --- count backends (other than myself) that are in
  *		active transactions.  This is used as a heuristic to decide if
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index 68c615afc6..61441bbf49 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/storage/procarray.h,v 1.6 2005/10/15 02:49:46 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/storage/procarray.h,v 1.7 2005/12/16 04:03:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,6 +29,7 @@ extern TransactionId GetOldestXmin(bool allDbs);
 extern PGPROC *BackendPidGetProc(int pid);
 extern int	BackendXidGetPid(TransactionId xid);
 extern bool IsBackendPid(int pid);
+extern int *GetAllBackendPids(void);
 extern bool DatabaseHasActiveBackends(Oid databaseId, bool ignoreMyself);
 
 extern int	CountActiveBackends(void);
-- 
2.24.1