Skip to content

Commit 511540d

Browse files
committed
Move isolationtester's is-blocked query into C code for speed.
Commit 4deb413 modified isolationtester's query to see whether a session is blocked to also check for waits occurring in GetSafeSnapshot. However, it did that in a way that enormously increased the query's runtime under CLOBBER_CACHE_ALWAYS, causing the buildfarm members that use that to run about four times slower than before, and in some cases fail entirely. To fix, push the entire logic into a dedicated backend function. This should actually reduce the CLOBBER_CACHE_ALWAYS runtime from what it was previously, though I've not checked that. In passing, expose a SQL function to check for safe-snapshot blockage, comparable to pg_blocking_pids. This is more or less free given the infrastructure built to solve the other problem, so we might as well. Thomas Munro Discussion: https://postgr.es/m/20170407165749.pstcakbc637opkax@alap3.anarazel.de
1 parent 9cf5c31 commit 511540d

File tree

8 files changed

+206
-13
lines changed

8 files changed

+206
-13
lines changed

doc/src/sgml/func.sgml

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15747,7 +15747,7 @@ SELECT * FROM pg_ls_dir('.') WITH ORDINALITY AS t(ls,n);
1574715747
<row>
1574815748
<entry><literal><function>pg_blocking_pids(<type>int</type>)</function></literal></entry>
1574915749
<entry><type>int[]</type></entry>
15750-
<entry>Process ID(s) that are blocking specified server process ID</entry>
15750+
<entry>Process ID(s) that are blocking specified server process ID from acquiring a lock</entry>
1575115751
</row>
1575215752

1575315753
<row>
@@ -15793,6 +15793,12 @@ SELECT * FROM pg_ls_dir('.') WITH ORDINALITY AS t(ls,n);
1579315793
<entry>server start time</entry>
1579415794
</row>
1579515795

15796+
<row>
15797+
<entry><literal><function>pg_safe_snapshot_blocking_pids(<type>int</type>)</function></literal></entry>
15798+
<entry><type>int[]</type></entry>
15799+
<entry>Process ID(s) that are blocking specified server process ID from acquiring a safe snapshot</entry>
15800+
</row>
15801+
1579615802
<row>
1579715803
<entry><literal><function>pg_trigger_depth()</function></literal></entry>
1579815804
<entry><type>int</type></entry>
@@ -16067,6 +16073,25 @@ SET search_path TO <replaceable>schema</> <optional>, <replaceable>schema</>, ..
1606716073
server started.
1606816074
</para>
1606916075

16076+
<indexterm>
16077+
<primary>pg_safe_snapshot_blocking_pids</primary>
16078+
</indexterm>
16079+
16080+
<para>
16081+
<function>pg_safe_snapshot_blocking_pids</function> returns an array of
16082+
the process IDs of the sessions that are blocking the server process with
16083+
the specified process ID from acquiring a safe snapshot, or an empty array
16084+
if there is no such server process or it is not blocked. A session
16085+
running a <literal>SERIALIZABLE</literal> transaction blocks
16086+
a <literal>SERIALIZABLE READ ONLY DEFERRABLE</literal> transaction from
16087+
acquiring a snapshot until the latter determines that it is safe to avoid
16088+
taking any predicate locks. See <xref linkend="xact-serializable"> for
16089+
more information about serializable and deferrable transactions. Frequent
16090+
calls to this function could have some impact on database performance,
16091+
because it needs access to the predicate lock manager's shared
16092+
state for a short time.
16093+
</para>
16094+
1607016095
<indexterm>
1607116096
<primary>version</primary>
1607216097
</indexterm>

src/backend/storage/lmgr/predicate.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1555,6 +1555,56 @@ GetSafeSnapshot(Snapshot origSnapshot)
15551555
return snapshot;
15561556
}
15571557

1558+
/*
1559+
* GetSafeSnapshotBlockingPids
1560+
* If the specified process is currently blocked in GetSafeSnapshot,
1561+
* write the process IDs of all processes that it is blocked by
1562+
* into the caller-supplied buffer output[]. The list is truncated at
1563+
* output_size, and the number of PIDs written into the buffer is
1564+
* returned. Returns zero if the given PID is not currently blocked
1565+
* in GetSafeSnapshot.
1566+
*/
1567+
int
1568+
GetSafeSnapshotBlockingPids(int blocked_pid, int *output, int output_size)
1569+
{
1570+
int num_written = 0;
1571+
SERIALIZABLEXACT *sxact;
1572+
1573+
LWLockAcquire(SerializableXactHashLock, LW_SHARED);
1574+
1575+
/* Find blocked_pid's SERIALIZABLEXACT by linear search. */
1576+
for (sxact = FirstPredXact(); sxact != NULL; sxact = NextPredXact(sxact))
1577+
{
1578+
if (sxact->pid == blocked_pid)
1579+
break;
1580+
}
1581+
1582+
/* Did we find it, and is it currently waiting in GetSafeSnapshot? */
1583+
if (sxact != NULL && SxactIsDeferrableWaiting(sxact))
1584+
{
1585+
RWConflict possibleUnsafeConflict;
1586+
1587+
/* Traverse the list of possible unsafe conflicts collecting PIDs. */
1588+
possibleUnsafeConflict = (RWConflict)
1589+
SHMQueueNext(&sxact->possibleUnsafeConflicts,
1590+
&sxact->possibleUnsafeConflicts,
1591+
offsetof(RWConflictData, inLink));
1592+
1593+
while (possibleUnsafeConflict != NULL && num_written < output_size)
1594+
{
1595+
output[num_written++] = possibleUnsafeConflict->sxactOut->pid;
1596+
possibleUnsafeConflict = (RWConflict)
1597+
SHMQueueNext(&sxact->possibleUnsafeConflicts,
1598+
&possibleUnsafeConflict->inLink,
1599+
offsetof(RWConflictData, inLink));
1600+
}
1601+
}
1602+
1603+
LWLockRelease(SerializableXactHashLock);
1604+
1605+
return num_written;
1606+
}
1607+
15581608
/*
15591609
* Acquire a snapshot that can be used for the current transaction.
15601610
*

src/backend/utils/adt/lockfuncs.c

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,125 @@ pg_blocking_pids(PG_FUNCTION_ARGS)
517517
}
518518

519519

520+
/*
521+
* pg_safe_snapshot_blocking_pids - produce an array of the PIDs blocking
522+
* given PID from getting a safe snapshot
523+
*
524+
* XXX this does not consider parallel-query cases; not clear how big a
525+
* problem that is in practice
526+
*/
527+
Datum
528+
pg_safe_snapshot_blocking_pids(PG_FUNCTION_ARGS)
529+
{
530+
int blocked_pid = PG_GETARG_INT32(0);
531+
int *blockers;
532+
int num_blockers;
533+
Datum *blocker_datums;
534+
535+
/* A buffer big enough for any possible blocker list without truncation */
536+
blockers = (int *) palloc(MaxBackends * sizeof(int));
537+
538+
/* Collect a snapshot of processes waited for by GetSafeSnapshot */
539+
num_blockers =
540+
GetSafeSnapshotBlockingPids(blocked_pid, blockers, MaxBackends);
541+
542+
/* Convert int array to Datum array */
543+
if (num_blockers > 0)
544+
{
545+
int i;
546+
547+
blocker_datums = (Datum *) palloc(num_blockers * sizeof(Datum));
548+
for (i = 0; i < num_blockers; ++i)
549+
blocker_datums[i] = Int32GetDatum(blockers[i]);
550+
}
551+
else
552+
blocker_datums = NULL;
553+
554+
/* Construct array, using hardwired knowledge about int4 type */
555+
PG_RETURN_ARRAYTYPE_P(construct_array(blocker_datums, num_blockers,
556+
INT4OID,
557+
sizeof(int32), true, 'i'));
558+
}
559+
560+
561+
/*
562+
* pg_isolation_test_session_is_blocked - support function for isolationtester
563+
*
564+
* Check if specified PID is blocked by any of the PIDs listed in the second
565+
* argument. Currently, this looks for blocking caused by waiting for
566+
* heavyweight locks or safe snapshots. We ignore blockage caused by PIDs
567+
* not directly under the isolationtester's control, eg autovacuum.
568+
*
569+
* This is an undocumented function intended for use by the isolation tester,
570+
* and may change in future releases as required for testing purposes.
571+
*/
572+
Datum
573+
pg_isolation_test_session_is_blocked(PG_FUNCTION_ARGS)
574+
{
575+
int blocked_pid = PG_GETARG_INT32(0);
576+
ArrayType *interesting_pids_a = PG_GETARG_ARRAYTYPE_P(1);
577+
ArrayType *blocking_pids_a;
578+
int32 *interesting_pids;
579+
int32 *blocking_pids;
580+
int num_interesting_pids;
581+
int num_blocking_pids;
582+
int dummy;
583+
int i,
584+
j;
585+
586+
/* Validate the passed-in array */
587+
Assert(ARR_ELEMTYPE(interesting_pids_a) == INT4OID);
588+
if (array_contains_nulls(interesting_pids_a))
589+
elog(ERROR, "array must not contain nulls");
590+
interesting_pids = (int32 *) ARR_DATA_PTR(interesting_pids_a);
591+
num_interesting_pids = ArrayGetNItems(ARR_NDIM(interesting_pids_a),
592+
ARR_DIMS(interesting_pids_a));
593+
594+
/*
595+
* Get the PIDs of all sessions blocking the given session's attempt to
596+
* acquire heavyweight locks.
597+
*/
598+
blocking_pids_a =
599+
DatumGetArrayTypeP(DirectFunctionCall1(pg_blocking_pids, blocked_pid));
600+
601+
Assert(ARR_ELEMTYPE(blocking_pids_a) == INT4OID);
602+
Assert(!array_contains_nulls(blocking_pids_a));
603+
blocking_pids = (int32 *) ARR_DATA_PTR(blocking_pids_a);
604+
num_blocking_pids = ArrayGetNItems(ARR_NDIM(blocking_pids_a),
605+
ARR_DIMS(blocking_pids_a));
606+
607+
/*
608+
* Check if any of these are in the list of interesting PIDs, that being
609+
* the sessions that the isolation tester is running. We don't use
610+
* "arrayoverlaps" here, because it would lead to cache lookups and one of
611+
* our goals is to run quickly under CLOBBER_CACHE_ALWAYS. We expect
612+
* blocking_pids to be usually empty and otherwise a very small number in
613+
* isolation tester cases, so make that the outer loop of a naive search
614+
* for a match.
615+
*/
616+
for (i = 0; i < num_blocking_pids; i++)
617+
for (j = 0; j < num_interesting_pids; j++)
618+
{
619+
if (blocking_pids[i] == interesting_pids[j])
620+
PG_RETURN_BOOL(true);
621+
}
622+
623+
/*
624+
* Check if blocked_pid is waiting for a safe snapshot. We could in
625+
* theory check the resulting array of blocker PIDs against the
626+
* interesting PIDs whitelist, but since there is no danger of autovacuum
627+
* blocking GetSafeSnapshot there seems to be no point in expending cycles
628+
* on allocating a buffer and searching for overlap; so it's presently
629+
* sufficient for the isolation tester's purposes to use a single element
630+
* buffer and check if the number of safe snapshot blockers is non-zero.
631+
*/
632+
if (GetSafeSnapshotBlockingPids(blocked_pid, &dummy, 1) > 0)
633+
PG_RETURN_BOOL(true);
634+
635+
PG_RETURN_BOOL(false);
636+
}
637+
638+
520639
/*
521640
* Functions for manipulating advisory locks
522641
*

src/include/catalog/catversion.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,6 @@
5353
*/
5454

5555
/* yyyymmddN */
56-
#define CATALOG_VERSION_NO 201704062
56+
#define CATALOG_VERSION_NO 201704101
5757

5858
#endif

src/include/catalog/pg_proc.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3139,7 +3139,11 @@ DESCR("show pg_hba.conf rules");
31393139
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_ ));
31403140
DESCR("view system lock information");
31413141
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_ ));
3142-
DESCR("get array of PIDs of sessions blocking specified backend PID");
3142+
DESCR("get array of PIDs of sessions blocking specified backend PID from acquiring a heavyweight lock");
3143+
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_ ));
3144+
DESCR("get array of PIDs of sessions blocking specified backend PID from acquiring a safe snapshot");
3145+
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_ ));
3146+
DESCR("isolationtester support function");
31433147
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_ ));
31443148
DESCR("view two-phase transactions");
31453149
DATA(insert OID = 3819 ( pg_get_multixact_members PGNSP PGUID 12 1 1000 0 0 f f f f t t v s 1 0 2249 "28" "{28,28,25}" "{i,o,o}" "{multixid,xid,mode}" _null_ _null_ pg_get_multixact_members _null_ _null_ _null_ ));

src/include/storage/predicate_internals.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,5 +474,7 @@ typedef struct TwoPhasePredicateRecord
474474
* locking internals.
475475
*/
476476
extern PredicateLockData *GetPredicateLockStatusData(void);
477+
extern int GetSafeSnapshotBlockingPids(int blocked_pid,
478+
int *output, int output_size);
477479

478480
#endif /* PREDICATE_INTERNALS_H */

src/test/isolation/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@
77
/specscanner.c
88

99
# Generated subdirectories
10+
/results/
1011
/output_iso/
1112
/tmp_check_iso/

src/test/isolation/isolationtester.c

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -224,20 +224,12 @@ main(int argc, char **argv)
224224
*/
225225
initPQExpBuffer(&wait_query);
226226
appendPQExpBufferStr(&wait_query,
227-
"SELECT pg_catalog.pg_blocking_pids($1) && '{");
227+
"SELECT pg_catalog.pg_isolation_test_session_is_blocked($1, '{");
228228
/* The spec syntax requires at least one session; assume that here. */
229229
appendPQExpBufferStr(&wait_query, backend_pids[1]);
230230
for (i = 2; i < nconns; i++)
231231
appendPQExpBuffer(&wait_query, ",%s", backend_pids[i]);
232-
appendPQExpBufferStr(&wait_query, "}'::integer[]");
233-
234-
/* Also detect certain wait events. */
235-
appendPQExpBufferStr(&wait_query,
236-
" OR EXISTS ("
237-
" SELECT * "
238-
" FROM pg_catalog.pg_stat_activity "
239-
" WHERE pid = $1 "
240-
" AND wait_event IN ('SafeSnapshot'))");
232+
appendPQExpBufferStr(&wait_query, "}')");
241233

242234
res = PQprepare(conns[0], PREP_WAITING, wait_query.data, 0, NULL);
243235
if (PQresultStatus(res) != PGRES_COMMAND_OK)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy