From 1d37828653c8657f718abe7cb61be49e907b7f99 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@enterprisedb.com>
Date: Mon, 10 Apr 2017 15:34:48 +1200
Subject: [PATCH] Provide a function for introspecting safe snapshot waits.

This patch introduces a new function pg_safe_snapshot_blocking_pids(),
modelled on pg_blocking_pids().  While the latter is concerned with waits
for heavyweight locks, this new function allows users of SERIALIZABLE READ
ONLY DEFERRABLE to see the PIDs of backends running concurrent SERIALIZABLE
transactions that are causing waits.
---
 doc/src/sgml/func.sgml            | 27 ++++++++++++++++++++++++++-
 src/backend/utils/adt/lockfuncs.c | 37 +++++++++++++++++++++++++++++++++++++
 src/include/catalog/pg_proc.h     |  4 +++-
 3 files changed, 66 insertions(+), 2 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index cb0a36a170f..1e5b0a1506e 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -15747,7 +15747,7 @@ SELECT * FROM pg_ls_dir('.') WITH ORDINALITY AS t(ls,n);
       <row>
        <entry><literal><function>pg_blocking_pids(<type>int</type>)</function></literal></entry>
        <entry><type>int[]</type></entry>
-       <entry>Process ID(s) that are blocking specified server process ID</entry>
+       <entry>Process ID(s) that are blocking specified server process ID from acquiring a lock</entry>
       </row>
 
       <row>
@@ -15794,6 +15794,12 @@ SELECT * FROM pg_ls_dir('.') WITH ORDINALITY AS t(ls,n);
       </row>
 
       <row>
+       <entry><literal><function>pg_safe_snapshot_blocking_pids(<type>int</type>)</function></literal></entry>
+       <entry><type>int[]</type></entry>
+       <entry>Process ID(s) that are blocking specified server process ID from acquiring a safe snapshot</entry>
+      </row>
+
+      <row>
        <entry><literal><function>pg_trigger_depth()</function></literal></entry>
        <entry><type>int</type></entry>
        <entry>current nesting level of <productname>PostgreSQL</> triggers
@@ -16068,6 +16074,25 @@ SET search_path TO <replaceable>schema</> <optional>, <replaceable>schema</>, ..
    </para>
 
    <indexterm>
+    <primary>pg_safe_snapshot_blocking_pids</primary>
+   </indexterm>
+
+   <para>
+    <function>pg_safe_snapshot_blocking_pids</function> returns an array of
+    the process IDs of the sessions that are blocking the server process with
+    the specified process ID from acquiring a safe snapshot, or an empty array
+    if there is no such server process or it is not blocked.  A session
+    running a <literal>SERIALIZABLE</literal> transaction blocks
+    a <literal>SERIALIZABLE READ ONLY DEFERRABLE</literal> transaction from
+    acquiring a snapshot until the latter determines that it is safe to avoid
+    taking any predicate locks.  See <xref linkend="xact-serializable"> for
+    more information about serializable and deferrable transactions.  Frequent
+    calls to this function could have some impact on database performance,
+    because it needs exclusive access to the predicate lock manager's shared
+    state for a short time.
+   </para>
+   
+   <indexterm>
     <primary>version</primary>
    </indexterm>
 
diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c
index 726ba73d427..193516554df 100644
--- a/src/backend/utils/adt/lockfuncs.c
+++ b/src/backend/utils/adt/lockfuncs.c
@@ -590,6 +590,43 @@ pg_isolation_test_session_is_blocked(PG_FUNCTION_ARGS)
 }
 
 /*
+ * pg_safe_snapshot_blocking_pids - produce an array of the PIDs blocking
+ * given PID in GetSafeSnapshot()
+ */
+Datum
+pg_safe_snapshot_blocking_pids(PG_FUNCTION_ARGS)
+{
+	int			blocked_pid = PG_GETARG_INT32(0);
+	int		   *blockers;
+	int			num_blockers;
+	Datum	   *blocker_datums;
+
+	/* A buffer big enough for any possible blocker list without truncation */
+	blockers = (int *) palloc(MaxBackends * sizeof(int));
+
+	/* Collect a snapshot of processes waited for by GetSafeSnapshot */
+	num_blockers =
+		GetSafeSnapshotBlockingPids(blocked_pid, blockers, MaxBackends);
+
+	/* Convert int array to Datum array */
+	if (num_blockers > 0)
+	{
+		int			i;
+
+		blocker_datums = (Datum *) palloc(num_blockers * sizeof(Datum));
+		for (i = 0; i < num_blockers; ++i)
+			blocker_datums[i] = Int32GetDatum(blockers[i]);
+	}
+	else
+		blocker_datums = NULL;
+
+	/* Convert Datum to Postgres array */
+	PG_RETURN_ARRAYTYPE_P(construct_array(blocker_datums, num_blockers,
+										  INT4OID,
+										  sizeof(int32), true, 'i'));
+}
+
+/*
  * Functions for manipulating advisory locks
  *
  * We make use of the locktag fields as follows:
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 58ad255d14b..620522816c0 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3139,7 +3139,9 @@ DESCR("show pg_hba.conf rules");
 DATA(insert OID = 1371 (  pg_lock_status   PGNSP PGUID 12 1 1000 0 0 f f f f t t v s 0 0 2249 "" "{25,26,26,23,21,25,28,26,26,21,25,23,25,16,16}" "{o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{locktype,database,relation,page,tuple,virtualxid,transactionid,classid,objid,objsubid,virtualtransaction,pid,mode,granted,fastpath}" _null_ _null_ pg_lock_status _null_ _null_ _null_ ));
 DESCR("view system lock information");
 DATA(insert OID = 2561 (  pg_blocking_pids PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 1007 "23" _null_ _null_ _null_ _null_ _null_ pg_blocking_pids _null_ _null_ _null_ ));
-DESCR("get array of PIDs of sessions blocking specified backend PID");
+DESCR("get array of PIDs of sessions blocking specified backend PID from acquiring a heavyweight lock");
+DATA(insert OID = 3376 (  pg_safe_snapshot_blocking_pids PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 1007 "23" _null_ _null_ _null_ _null_ _null_ pg_safe_snapshot_blocking_pids _null_ _null_ _null_ ));
+DESCR("get array of PIDs of sessions blocking specified backed PID from acquiring a safe snapshot");
 DATA(insert OID = 3378 (  pg_isolation_test_session_is_blocked PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 16 "23 1007" _null_ _null_ _null_ _null_ _null_ pg_isolation_test_session_is_blocked _null_ _null_ _null_ ));
 DESCR("is the given backend PID waiting for a safe snapshot, or for one of the given PIDs for a lock?");
 DATA(insert OID = 1065 (  pg_prepared_xact PGNSP PGUID 12 1 1000 0 0 f f f f t t v s 0 0 2249 "" "{28,25,1184,26,26}" "{o,o,o,o,o}" "{transaction,gid,prepared,ownerid,dbid}" _null_ _null_ pg_prepared_xact _null_ _null_ _null_ ));
-- 
2.12.2

