Обсуждение: Testing LISTEN/NOTIFY more effectively

Поиск
Список
Период
Сортировка

Testing LISTEN/NOTIFY more effectively

От
Tom Lane
Дата:
Since we have three or four different NOTIFY improvement proposals
floating around in the current CF, I got a bit distressed at the lack
of test coverage for that functionality.  While the code coverage
report makes it look like commands/async.c isn't so badly covered,
that's all coming from src/test/regress/sql/async.sql and
src/test/isolation/specs/async-notify.spec.  A look at those files
shows that nowhere is there any actual verification that "NOTIFY foo"
results in a report of "foo" being received; let alone any
more-advanced questions such as whether de-duplication of reports
happens.

The reason for this is that psql's report of a notification event
includes the sending backend's PID, making it impossible for the
test output to be stable; neither the core nor isolation regression
test frameworks can cope with unpredictable output.

We've occasionally batted around ideas for making it possible for
these test frameworks to verify not-entirely-fixed output, and that
would be a good thing to do, but I'm not volunteering for that today.

So, if we'd like to have more thorough NOTIFY coverage without going
to that much work, what to do?  I thought of a few alternatives:

1. Write a TAP test instead of using the old test frameworks, and
use regexps to check the expected output.  But this seems ugly and
hard to get right.  In particular, our TAP infrastructure doesn't
seem to be (easily?) capable of running concurrent psql sessions,
so it doesn't seem like there's any good way to test cross-session
notifies that way.

2. Change psql so that there's a way to get NOTIFY messages without
the sending PID.  For testing purposes, it'd be sufficient to know
whether the sending PID is our own backend's PID or not.  This idea
is not horrible, and it might even be useful for outside purposes
if we made it flexible enough; which leads to thoughts like allowing
the psql user to set a format-style string, similar to the PROMPT
strings but with escapes for channel name, payload, etc.  I foresee
bikeshedding, but we could probably come to an agreement on a feature
like that.

3. On the other hand, that doesn't help much for the isolation tester
because it doesn't go through psql.  In fact, AFAICS it doesn't have
any provision for dealing with notify messages at all; probably,
in the async-notify.spec test, the listening session builds up a
queue of notifies that it never reads.  So we could imagine addressing
the testing gap strictly inside the isolation-tester framework, if we
added the ability for it to detect and print notifications in a
test-friendly format (no explicit PIDs).

I'm finding alternative #3 the most attractive, because we really
want isolation-style testing for LISTEN/NOTIFY, and this solution
doesn't require designing a psql feature that we'd need to get
consensus on.

Before I start coding that, any thoughts or better ideas?

            regards, tom lane



Re: Testing LISTEN/NOTIFY more effectively

От
Andres Freund
Дата:
Hi,

On 2019-07-27 12:46:51 -0400, Tom Lane wrote:
> So, if we'd like to have more thorough NOTIFY coverage without going
> to that much work, what to do?  I thought of a few alternatives:
> 
> 1. Write a TAP test instead of using the old test frameworks, and
> use regexps to check the expected output.  But this seems ugly and
> hard to get right.  In particular, our TAP infrastructure doesn't
> seem to be (easily?) capable of running concurrent psql sessions,
> so it doesn't seem like there's any good way to test cross-session
> notifies that way.

It's not that hard to have concurrent psql sessions -
e.g. src/test/recovery/t/013_crash_restart.pl does so. Writing tests by
interactively controlling psql is pretty painful regardless.

I'm inclined to think that this is better tested using isolationtester
than a tap test.


> 2. Change psql so that there's a way to get NOTIFY messages without
> the sending PID.  For testing purposes, it'd be sufficient to know
> whether the sending PID is our own backend's PID or not.  This idea
> is not horrible, and it might even be useful for outside purposes
> if we made it flexible enough; which leads to thoughts like allowing
> the psql user to set a format-style string, similar to the PROMPT
> strings but with escapes for channel name, payload, etc.  I foresee
> bikeshedding, but we could probably come to an agreement on a feature
> like that.

I was wondering about just tying it to VERBOSITY. But that'd not allow
us to see whether our backend was the sender. I'm mildly inclined to
think that that might still be a good idea, even if we mostly go with
3) - some basic plain regression test coverage of actually receiving
notifies would be good.


> 3. On the other hand, that doesn't help much for the isolation tester
> because it doesn't go through psql.  In fact, AFAICS it doesn't have
> any provision for dealing with notify messages at all; probably,
> in the async-notify.spec test, the listening session builds up a
> queue of notifies that it never reads.  So we could imagine addressing
> the testing gap strictly inside the isolation-tester framework, if we
> added the ability for it to detect and print notifications in a
> test-friendly format (no explicit PIDs).
> 
> I'm finding alternative #3 the most attractive, because we really
> want isolation-style testing for LISTEN/NOTIFY, and this solution
> doesn't require designing a psql feature that we'd need to get
> consensus on.

Yea. I think that's really what need. As you say, the type of test we
really need is what isolationtester provides. We can reimplement it
awkwardly in perl, but there seems to be little point in doing so.
Especially as what we're talking about is an additional ~15 lines or so
of code in isolationtester.

It'd be kinda neat if we had other information in the notify
message. E.g. having access to the sender's application name would be
useful for isolationtester, to actually verify where the message came
from. But it's probably not worth investing a lot in that.

Perhaps we could just have isolationtester check to which
isolationtester session the backend pid belongs? And then print the
session name instead of the pid? That should be fairly easy, and would
probably give us all we need?

Greetings,

Andres Freund



Re: Testing LISTEN/NOTIFY more effectively

От
Tom Lane
Дата:
Andres Freund <andres@anarazel.de> writes:
> On 2019-07-27 12:46:51 -0400, Tom Lane wrote:
>> I'm finding alternative #3 the most attractive, because we really
>> want isolation-style testing for LISTEN/NOTIFY, and this solution
>> doesn't require designing a psql feature that we'd need to get
>> consensus on.

> Perhaps we could just have isolationtester check to which
> isolationtester session the backend pid belongs? And then print the
> session name instead of the pid? That should be fairly easy, and would
> probably give us all we need?

Oh, that's a good idea -- it's already tracking all the backend PIDs,
so probably not much extra work to do it like that.

            regards, tom lane



Re: Testing LISTEN/NOTIFY more effectively

От
Tom Lane
Дата:
While I'm looking at isolationtester ... my eye was immediately drawn
to this bit, because it claims to be dealing with NOTIFY messages ---
though that's wrong, it's really blocking NOTICE messages:

        /*
         * Suppress NOTIFY messages, which otherwise pop into results at odd
         * places.
         */
        res = PQexec(conns[i], "SET client_min_messages = warning;");
        if (PQresultStatus(res) != PGRES_COMMAND_OK)
        {
            fprintf(stderr, "message level setup failed: %s", PQerrorMessage(conns[i]));
            exit(1);
        }
        PQclear(res);

This seems to me to be a great example of terrible test design.
It's not isolationtester's job to impose a client_min_messages level
on the test scripts; if they want a non-default level, they can
perfectly well set it for themselves in their setup sections.
Furthermore, if I remove this bit, the only NOTICE messages I'm
actually seeing come from explicit RAISE NOTICE messages in the
test scripts themselves, which means this is overriding the express
intent of individual test authors.  And my testing isn't detecting
any instability in when those come out, although of course the
buildfarm might have a different opinion.

So I think we should apply something like the attached, and if the
buildfarm shows any instability as a result, dealing with that by
taking out the RAISE NOTICE commands.

I'm a little inclined to remove the notice anyway in the
plpgsql-toast test, as the bulk-to-value ratio doesn't seem good.

            regards, tom lane

diff --git a/src/test/isolation/expected/insert-conflict-specconflict.out
b/src/test/isolation/expected/insert-conflict-specconflict.out
index 5726bdb..20cc421 100644
--- a/src/test/isolation/expected/insert-conflict-specconflict.out
+++ b/src/test/isolation/expected/insert-conflict-specconflict.out
@@ -13,7 +13,11 @@ pg_advisory_locksess           lock
 step controller_show: SELECT * FROM upserttest;
 key            data

+s1: NOTICE:  called for k1
+s1: NOTICE:  blocking 3
 step s1_upsert: INSERT INTO upserttest(key, data) VALUES('k1', 'inserted s1') ON CONFLICT (blurt_and_lock(key)) DO
UPDATESET data = upserttest.data || ' with conflict update s1'; <waiting ...> 
+s2: NOTICE:  called for k1
+s2: NOTICE:  blocking 3
 step s2_upsert: INSERT INTO upserttest(key, data) VALUES('k1', 'inserted s2') ON CONFLICT (blurt_and_lock(key)) DO
UPDATESET data = upserttest.data || ' with conflict update s2'; <waiting ...> 
 step controller_show: SELECT * FROM upserttest;
 key            data
@@ -30,10 +34,14 @@ step controller_unlock_1_3: SELECT pg_advisory_unlock(1, 3);
 pg_advisory_unlock

 t
+s1: NOTICE:  called for k1
+s1: NOTICE:  blocking 2
 step controller_unlock_2_3: SELECT pg_advisory_unlock(2, 3);
 pg_advisory_unlock

 t
+s2: NOTICE:  called for k1
+s2: NOTICE:  blocking 2
 step controller_show: SELECT * FROM upserttest;
 key            data

@@ -50,6 +58,10 @@ step controller_unlock_1_2: SELECT pg_advisory_unlock(1, 2);
 pg_advisory_unlock

 t
+s1: NOTICE:  called for k1
+s1: NOTICE:  blocking 2
+s1: NOTICE:  called for k1
+s1: NOTICE:  blocking 2
 step s1_upsert: <... completed>
 step controller_show: SELECT * FROM upserttest;
 key            data
@@ -69,7 +81,11 @@ pg_advisory_locksess           lock
 step controller_show: SELECT * FROM upserttest;
 key            data

+s1: NOTICE:  called for k1
+s1: NOTICE:  blocking 3
 step s1_upsert: INSERT INTO upserttest(key, data) VALUES('k1', 'inserted s1') ON CONFLICT (blurt_and_lock(key)) DO
UPDATESET data = upserttest.data || ' with conflict update s1'; <waiting ...> 
+s2: NOTICE:  called for k1
+s2: NOTICE:  blocking 3
 step s2_upsert: INSERT INTO upserttest(key, data) VALUES('k1', 'inserted s2') ON CONFLICT (blurt_and_lock(key)) DO
UPDATESET data = upserttest.data || ' with conflict update s2'; <waiting ...> 
 step controller_show: SELECT * FROM upserttest;
 key            data
@@ -86,10 +102,14 @@ step controller_unlock_1_3: SELECT pg_advisory_unlock(1, 3);
 pg_advisory_unlock

 t
+s1: NOTICE:  called for k1
+s1: NOTICE:  blocking 2
 step controller_unlock_2_3: SELECT pg_advisory_unlock(2, 3);
 pg_advisory_unlock

 t
+s2: NOTICE:  called for k1
+s2: NOTICE:  blocking 2
 step controller_show: SELECT * FROM upserttest;
 key            data

@@ -106,6 +126,10 @@ step controller_unlock_2_2: SELECT pg_advisory_unlock(2, 2);
 pg_advisory_unlock

 t
+s2: NOTICE:  called for k1
+s2: NOTICE:  blocking 2
+s2: NOTICE:  called for k1
+s2: NOTICE:  blocking 2
 step s2_upsert: <... completed>
 step controller_show: SELECT * FROM upserttest;
 key            data
@@ -127,7 +151,11 @@ key            data

 step s1_begin: BEGIN;
 step s2_begin: BEGIN;
+s1: NOTICE:  called for k1
+s1: NOTICE:  blocking 3
 step s1_upsert: INSERT INTO upserttest(key, data) VALUES('k1', 'inserted s1') ON CONFLICT (blurt_and_lock(key)) DO
UPDATESET data = upserttest.data || ' with conflict update s1'; <waiting ...> 
+s2: NOTICE:  called for k1
+s2: NOTICE:  blocking 3
 step s2_upsert: INSERT INTO upserttest(key, data) VALUES('k1', 'inserted s2') ON CONFLICT (blurt_and_lock(key)) DO
UPDATESET data = upserttest.data || ' with conflict update s2'; <waiting ...> 
 step controller_show: SELECT * FROM upserttest;
 key            data
@@ -144,10 +172,14 @@ step controller_unlock_1_3: SELECT pg_advisory_unlock(1, 3);
 pg_advisory_unlock

 t
+s1: NOTICE:  called for k1
+s1: NOTICE:  blocking 2
 step controller_unlock_2_3: SELECT pg_advisory_unlock(2, 3);
 pg_advisory_unlock

 t
+s2: NOTICE:  called for k1
+s2: NOTICE:  blocking 2
 step controller_show: SELECT * FROM upserttest;
 key            data

@@ -163,10 +195,16 @@ step controller_unlock_2_2: SELECT pg_advisory_unlock(2, 2);
 pg_advisory_unlock

 t
+s2: NOTICE:  called for k1
+s2: NOTICE:  blocking 2
+s2: NOTICE:  called for k1
+s2: NOTICE:  blocking 2
 step controller_show: SELECT * FROM upserttest;
 key            data

 step s1_commit: COMMIT;
+s2: NOTICE:  called for k1
+s2: NOTICE:  blocking 2
 step s2_upsert: <... completed>
 step controller_show: SELECT * FROM upserttest;
 key            data
diff --git a/src/test/isolation/expected/plpgsql-toast.out b/src/test/isolation/expected/plpgsql-toast.out
index 4341153..39a7bbe 100644
--- a/src/test/isolation/expected/plpgsql-toast.out
+++ b/src/test/isolation/expected/plpgsql-toast.out
@@ -35,6 +35,7 @@ step unlock:
 pg_advisory_unlock

 t
+s1: NOTICE:  x =
foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo
 step assign1: <... completed>

 starting permutation: lock assign2 vacuum unlock
@@ -72,6 +73,7 @@ step unlock:
 pg_advisory_unlock

 t
+s1: NOTICE:  x =
foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo
 step assign2: <... completed>

 starting permutation: lock assign3 vacuum unlock
@@ -110,6 +112,7 @@ step unlock:
 pg_advisory_unlock

 t
+s1: NOTICE:  r =
(1,foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo)
 step assign3: <... completed>

 starting permutation: lock assign4 vacuum unlock
@@ -147,6 +150,7 @@ step unlock:
 pg_advisory_unlock

 t
+s1: NOTICE:  r =
(1,foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo)
 step assign4: <... completed>

 starting permutation: lock assign5 vacuum unlock
@@ -186,4 +190,5 @@ step unlock:
 pg_advisory_unlock

 t
+s1: NOTICE:  r =
(foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo)
 step assign5: <... completed>
diff --git a/src/test/isolation/isolationtester.c b/src/test/isolation/isolationtester.c
index 2f039b8..9a89468 100644
--- a/src/test/isolation/isolationtester.c
+++ b/src/test/isolation/isolationtester.c
@@ -187,18 +187,6 @@ main(int argc, char **argv)
                                  blackholeNoticeProcessor,
                                  NULL);

-        /*
-         * Suppress NOTIFY messages, which otherwise pop into results at odd
-         * places.
-         */
-        res = PQexec(conns[i], "SET client_min_messages = warning;");
-        if (PQresultStatus(res) != PGRES_COMMAND_OK)
-        {
-            fprintf(stderr, "message level setup failed: %s", PQerrorMessage(conns[i]));
-            exit(1);
-        }
-        PQclear(res);
-
         /* Get the backend pid for lock wait checking. */
         res = PQexec(conns[i], "SELECT pg_catalog.pg_backend_pid()");
         if (PQresultStatus(res) == PGRES_TUPLES_OK)

Re: Testing LISTEN/NOTIFY more effectively

От
Andres Freund
Дата:
Hi,

On 2019-07-27 14:15:39 -0400, Tom Lane wrote:
> So I think we should apply something like the attached, and if the
> buildfarm shows any instability as a result, dealing with that by
> taking out the RAISE NOTICE commands.

+1

> diff --git a/src/test/isolation/expected/insert-conflict-specconflict.out
b/src/test/isolation/expected/insert-conflict-specconflict.out
> index 5726bdb..20cc421 100644
> --- a/src/test/isolation/expected/insert-conflict-specconflict.out
> +++ b/src/test/isolation/expected/insert-conflict-specconflict.out
> @@ -13,7 +13,11 @@ pg_advisory_locksess           lock
>  step controller_show: SELECT * FROM upserttest;
>  key            data           
>  
> +s1: NOTICE:  called for k1
> +s1: NOTICE:  blocking 3
>  step s1_upsert: INSERT INTO upserttest(key, data) VALUES('k1', 'inserted s1') ON CONFLICT (blurt_and_lock(key)) DO
UPDATESET data = upserttest.data || ' with conflict update s1'; <waiting ...>
 
> +s2: NOTICE:  called for k1
> +s2: NOTICE:  blocking 3

Hm, that actually makes the test - which is pretty complicated - easier
to understand.


> diff --git a/src/test/isolation/expected/plpgsql-toast.out b/src/test/isolation/expected/plpgsql-toast.out
> index 4341153..39a7bbe 100644
> --- a/src/test/isolation/expected/plpgsql-toast.out
> +++ b/src/test/isolation/expected/plpgsql-toast.out
> @@ -35,6 +35,7 @@ step unlock:
>  pg_advisory_unlock
>  
>  t              
> +s1: NOTICE:  x = foofoofoofo

Yea, there indeed does not not much point in this.

Greetings,

Andres Freund



Re: Testing LISTEN/NOTIFY more effectively

От
Tom Lane
Дата:
Andres Freund <andres@anarazel.de> writes:
> On 2019-07-27 14:15:39 -0400, Tom Lane wrote:
>> So I think we should apply something like the attached, and if the
>> buildfarm shows any instability as a result, dealing with that by
>> taking out the RAISE NOTICE commands.

> +1

>> diff --git a/src/test/isolation/expected/insert-conflict-specconflict.out
b/src/test/isolation/expected/insert-conflict-specconflict.out
>> index 5726bdb..20cc421 100644
>> --- a/src/test/isolation/expected/insert-conflict-specconflict.out
>> +++ b/src/test/isolation/expected/insert-conflict-specconflict.out
>> @@ -13,7 +13,11 @@ pg_advisory_locksess           lock
>> step controller_show: SELECT * FROM upserttest;
>> key            data
>>
>> +s1: NOTICE:  called for k1
>> +s1: NOTICE:  blocking 3
>> step s1_upsert: INSERT INTO upserttest(key, data) VALUES('k1', 'inserted s1') ON CONFLICT (blurt_and_lock(key)) DO
UPDATESET data = upserttest.data || ' with conflict update s1'; <waiting ...> 
>> +s2: NOTICE:  called for k1
>> +s2: NOTICE:  blocking 3

> Hm, that actually makes the test - which is pretty complicated - easier
> to understand.

Unfortunately, I just found out that on a slow enough machine
(prairiedog's host) there *is* some variation in when that test's
notices come out.  I am unsure whether that's to be expected or
whether there's something wrong there --- Peter, any thoughts?

What I will do for the moment is remove the client_min_messages=WARNING
setting from isolationtester.c and instead put it into
insert-conflict-specconflict.spec, which seems like a saner
way to manage this.  If we can get these messages to appear
stably, we can just fix that spec file.

>> +s1: NOTICE:  x = foofoofoofo

> Yea, there indeed does not not much point in this.

Maybe we could just log the lengths of the strings... if there's
anything broken, we could expect that the decompressed output
would be a different length.

            regards, tom lane



Re: Testing LISTEN/NOTIFY more effectively

От
Andres Freund
Дата:
Hi,

On 2019-07-27 15:39:44 -0400, Tom Lane wrote:
> Andres Freund <andres@anarazel.de> writes:
> >> diff --git a/src/test/isolation/expected/insert-conflict-specconflict.out
b/src/test/isolation/expected/insert-conflict-specconflict.out
> >> index 5726bdb..20cc421 100644
> >> --- a/src/test/isolation/expected/insert-conflict-specconflict.out
> >> +++ b/src/test/isolation/expected/insert-conflict-specconflict.out
> >> @@ -13,7 +13,11 @@ pg_advisory_locksess           lock
> >> step controller_show: SELECT * FROM upserttest;
> >> key            data           
> >> 
> >> +s1: NOTICE:  called for k1
> >> +s1: NOTICE:  blocking 3
> >> step s1_upsert: INSERT INTO upserttest(key, data) VALUES('k1', 'inserted s1') ON CONFLICT (blurt_and_lock(key)) DO
UPDATESET data = upserttest.data || ' with conflict update s1'; <waiting ...>
 
> >> +s2: NOTICE:  called for k1
> >> +s2: NOTICE:  blocking 3
> 
> > Hm, that actually makes the test - which is pretty complicated - easier
> > to understand.
> 
> Unfortunately, I just found out that on a slow enough machine
> (prairiedog's host) there *is* some variation in when that test's
> notices come out.  I am unsure whether that's to be expected or
> whether there's something wrong there

Hm. Any chance you could show the diff? I don't immediately see why.


>  --- Peter, any thoughts?

Think that's my transgression :/


> What I will do for the moment is remove the client_min_messages=WARNING
> setting from isolationtester.c and instead put it into
> insert-conflict-specconflict.spec, which seems like a saner
> way to manage this.  If we can get these messages to appear
> stably, we can just fix that spec file.

Makes sense.

Greetings,

Andres Freund



Re: Testing LISTEN/NOTIFY more effectively

От
Peter Geoghegan
Дата:
On Sat, Jul 27, 2019 at 12:39 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Unfortunately, I just found out that on a slow enough machine
> (prairiedog's host) there *is* some variation in when that test's
> notices come out.  I am unsure whether that's to be expected or
> whether there's something wrong there --- Peter, any thoughts?

I don't know why this happens, but it's worth noting that the plpgsql
function that raises these notices ("blurt_and_lock()") is marked
IMMUTABLE (not sure if you noticed that already). This is a deliberate
misrepresentation which is needed to acquire advisory locks at just
the right points during execution.

If I had to guess, I'd guess that it had something to do with that. I
might be able to come up with a better explanation if I saw the diff.

-- 
Peter Geoghegan



Re: Testing LISTEN/NOTIFY more effectively

От
Tom Lane
Дата:
I wrote:
> Andres Freund <andres@anarazel.de> writes:
>> Perhaps we could just have isolationtester check to which
>> isolationtester session the backend pid belongs? And then print the
>> session name instead of the pid? That should be fairly easy, and would
>> probably give us all we need?

> Oh, that's a good idea -- it's already tracking all the backend PIDs,
> so probably not much extra work to do it like that.

I found out that to avoid confusion, one really wants the message to
identify both the sending and receiving sessions.  Here's a patch
that does it that way and extends the async-notify.spec test to
perform basic end-to-end checks on LISTEN/NOTIFY.

I intentionally made the test show the lack of NOTIFY de-deduplication
that currently happens with subtransactions.  If we change this as I
proposed in <17822.1564186806@sss.pgh.pa.us>, this test output will
change.

            regards, tom lane

diff --git a/src/test/isolation/expected/async-notify.out b/src/test/isolation/expected/async-notify.out
index 92d281a..3dc1227 100644
--- a/src/test/isolation/expected/async-notify.out
+++ b/src/test/isolation/expected/async-notify.out
@@ -1,17 +1,82 @@
 Parsed test spec with 2 sessions

-starting permutation: listen begin check notify check
-step listen: LISTEN a;
-step begin: BEGIN;
-step check: SELECT pg_notification_queue_usage() > 0 AS nonzero;
+starting permutation: listenc notify1 notify2 notify3
+step listenc: LISTEN c1; LISTEN c2;
+step notify1: NOTIFY c1;
+notifier: NOTIFY "c1" with payload "" from notifier
+step notify2: NOTIFY c2, 'payload';
+notifier: NOTIFY "c2" with payload "payload" from notifier
+step notify3: NOTIFY c3, 'payload3';
+
+starting permutation: listenc notifyd notifys1
+step listenc: LISTEN c1; LISTEN c2;
+step notifyd: NOTIFY c2, 'payload'; NOTIFY c1; NOTIFY "c2", 'payload';
+notifier: NOTIFY "c2" with payload "payload" from notifier
+notifier: NOTIFY "c1" with payload "" from notifier
+step notifys1:
+    BEGIN;
+    NOTIFY c1, 'payload1'; NOTIFY "c2", 'payload2';
+    NOTIFY c1, 'payload1'; NOTIFY "c2", 'payload2';
+    SAVEPOINT s1;
+    NOTIFY c1, 'payload1'; NOTIFY "c2", 'payload2';
+    NOTIFY c1, 'payload1s'; NOTIFY "c2", 'payload2s';
+    NOTIFY c1, 'payload1'; NOTIFY "c2", 'payload2';
+    NOTIFY c1, 'payload1s'; NOTIFY "c2", 'payload2s';
+    RELEASE SAVEPOINT s1;
+    SAVEPOINT s2;
+    NOTIFY c1, 'rpayload1'; NOTIFY "c2", 'rpayload2';
+    NOTIFY c1, 'rpayload1s'; NOTIFY "c2", 'rpayload2s';
+    NOTIFY c1, 'rpayload1'; NOTIFY "c2", 'rpayload2';
+    NOTIFY c1, 'rpayload1s'; NOTIFY "c2", 'rpayload2s';
+    ROLLBACK TO SAVEPOINT s2;
+    COMMIT;
+
+notifier: NOTIFY "c1" with payload "payload1" from notifier
+notifier: NOTIFY "c2" with payload "payload2" from notifier
+notifier: NOTIFY "c1" with payload "payload1" from notifier
+notifier: NOTIFY "c2" with payload "payload2" from notifier
+notifier: NOTIFY "c1" with payload "payload1s" from notifier
+notifier: NOTIFY "c2" with payload "payload2s" from notifier
+
+starting permutation: llisten notify1 notify2 notify3 lcheck
+step llisten: LISTEN c1; LISTEN c2;
+step notify1: NOTIFY c1;
+step notify2: NOTIFY c2, 'payload';
+step notify3: NOTIFY c3, 'payload3';
+step lcheck: SELECT 1 AS x;
+x
+
+1
+listener: NOTIFY "c1" with payload "" from notifier
+listener: NOTIFY "c2" with payload "payload" from notifier
+
+starting permutation: listenc llisten notify1 notify2 notify3 lcheck
+step listenc: LISTEN c1; LISTEN c2;
+step llisten: LISTEN c1; LISTEN c2;
+step notify1: NOTIFY c1;
+notifier: NOTIFY "c1" with payload "" from notifier
+step notify2: NOTIFY c2, 'payload';
+notifier: NOTIFY "c2" with payload "payload" from notifier
+step notify3: NOTIFY c3, 'payload3';
+step lcheck: SELECT 1 AS x;
+x
+
+1
+listener: NOTIFY "c1" with payload "" from notifier
+listener: NOTIFY "c2" with payload "payload" from notifier
+
+starting permutation: llisten lbegin usage bignotify usage
+step llisten: LISTEN c1; LISTEN c2;
+step lbegin: BEGIN;
+step usage: SELECT pg_notification_queue_usage() > 0 AS nonzero;
 nonzero

 f
-step notify: SELECT count(pg_notify('a', s::text)) FROM generate_series(1, 1000) s;
+step bignotify: SELECT count(pg_notify('c1', s::text)) FROM generate_series(1, 1000) s;
 count

 1000
-step check: SELECT pg_notification_queue_usage() > 0 AS nonzero;
+step usage: SELECT pg_notification_queue_usage() > 0 AS nonzero;
 nonzero

 t
diff --git a/src/test/isolation/isolationtester.c b/src/test/isolation/isolationtester.c
index 6ab19b1..98e5bf2 100644
--- a/src/test/isolation/isolationtester.c
+++ b/src/test/isolation/isolationtester.c
@@ -23,10 +23,12 @@

 /*
  * conns[0] is the global setup, teardown, and watchdog connection.  Additional
- * connections represent spec-defined sessions.
+ * connections represent spec-defined sessions.  We also track the backend
+ * PID, in numeric and string formats, for each connection.
  */
 static PGconn **conns = NULL;
-static const char **backend_pids = NULL;
+static int *backend_pids = NULL;
+static const char **backend_pid_strs = NULL;
 static int    nconns = 0;

 /* In dry run only output permutations to be run by the tester. */
@@ -41,7 +43,7 @@ static void run_permutation(TestSpec *testspec, int nsteps, Step **steps);

 #define STEP_NONBLOCK    0x1        /* return 0 as soon as cmd waits for a lock */
 #define STEP_RETRY        0x2        /* this is a retry of a previously-waiting cmd */
-static bool try_complete_step(Step *step, int flags);
+static bool try_complete_step(TestSpec *testspec, Step *step, int flags);

 static int    step_qsort_cmp(const void *a, const void *b);
 static int    step_bsearch_cmp(const void *a, const void *b);
@@ -159,9 +161,11 @@ main(int argc, char **argv)
      * extra for lock wait detection and global work.
      */
     nconns = 1 + testspec->nsessions;
-    conns = calloc(nconns, sizeof(PGconn *));
+    conns = (PGconn **) pg_malloc0(nconns * sizeof(PGconn *));
+    backend_pids = pg_malloc0(nconns * sizeof(*backend_pids));
+    backend_pid_strs = pg_malloc0(nconns * sizeof(*backend_pid_strs));
     atexit(disconnect_atexit);
-    backend_pids = calloc(nconns, sizeof(*backend_pids));
+
     for (i = 0; i < nconns; i++)
     {
         conns[i] = PQconnectdb(conninfo);
@@ -187,26 +191,9 @@ main(int argc, char **argv)
                                  blackholeNoticeProcessor,
                                  NULL);

-        /* Get the backend pid for lock wait checking. */
-        res = PQexec(conns[i], "SELECT pg_catalog.pg_backend_pid()");
-        if (PQresultStatus(res) == PGRES_TUPLES_OK)
-        {
-            if (PQntuples(res) == 1 && PQnfields(res) == 1)
-                backend_pids[i] = pg_strdup(PQgetvalue(res, 0, 0));
-            else
-            {
-                fprintf(stderr, "backend pid query returned %d rows and %d columns, expected 1 row and 1 column",
-                        PQntuples(res), PQnfields(res));
-                exit(1);
-            }
-        }
-        else
-        {
-            fprintf(stderr, "backend pid query failed: %s",
-                    PQerrorMessage(conns[i]));
-            exit(1);
-        }
-        PQclear(res);
+        /* Save each connection's backend PID for subsequent use. */
+        backend_pids[i] = PQbackendPID(conns[i]);
+        backend_pid_strs[i] = psprintf("%d", backend_pids[i]);
     }

     /* Set the session index fields in steps. */
@@ -231,9 +218,9 @@ main(int argc, char **argv)
     appendPQExpBufferStr(&wait_query,
                          "SELECT pg_catalog.pg_isolation_test_session_is_blocked($1, '{");
     /* The spec syntax requires at least one session; assume that here. */
-    appendPQExpBufferStr(&wait_query, backend_pids[1]);
+    appendPQExpBufferStr(&wait_query, backend_pid_strs[1]);
     for (i = 2; i < nconns; i++)
-        appendPQExpBuffer(&wait_query, ",%s", backend_pids[i]);
+        appendPQExpBuffer(&wait_query, ",%s", backend_pid_strs[i]);
     appendPQExpBufferStr(&wait_query, "}')");

     res = PQprepare(conns[0], PREP_WAITING, wait_query.data, 0, NULL);
@@ -549,7 +536,7 @@ run_permutation(TestSpec *testspec, int nsteps, Step **steps)
                 oldstep = waiting[w];

                 /* Wait for previous step on this connection. */
-                try_complete_step(oldstep, STEP_RETRY);
+                try_complete_step(testspec, oldstep, STEP_RETRY);

                 /* Remove that step from the waiting[] array. */
                 if (w + 1 < nwaiting)
@@ -571,7 +558,8 @@ run_permutation(TestSpec *testspec, int nsteps, Step **steps)
             nerrorstep = 0;
             while (w < nwaiting)
             {
-                if (try_complete_step(waiting[w], STEP_NONBLOCK | STEP_RETRY))
+                if (try_complete_step(testspec, waiting[w],
+                                      STEP_NONBLOCK | STEP_RETRY))
                 {
                     /* Still blocked on a lock, leave it alone. */
                     w++;
@@ -600,14 +588,15 @@ run_permutation(TestSpec *testspec, int nsteps, Step **steps)
         }

         /* Try to complete this step without blocking.  */
-        mustwait = try_complete_step(step, STEP_NONBLOCK);
+        mustwait = try_complete_step(testspec, step, STEP_NONBLOCK);

         /* Check for completion of any steps that were previously waiting. */
         w = 0;
         nerrorstep = 0;
         while (w < nwaiting)
         {
-            if (try_complete_step(waiting[w], STEP_NONBLOCK | STEP_RETRY))
+            if (try_complete_step(testspec, waiting[w],
+                                  STEP_NONBLOCK | STEP_RETRY))
                 w++;
             else
             {
@@ -630,7 +619,7 @@ run_permutation(TestSpec *testspec, int nsteps, Step **steps)
     /* Wait for any remaining queries. */
     for (w = 0; w < nwaiting; ++w)
     {
-        try_complete_step(waiting[w], STEP_RETRY);
+        try_complete_step(testspec, waiting[w], STEP_RETRY);
         report_error_message(waiting[w]);
     }

@@ -693,7 +682,7 @@ run_permutation(TestSpec *testspec, int nsteps, Step **steps)
  * a lock, returns true.  Otherwise, returns false.
  */
 static bool
-try_complete_step(Step *step, int flags)
+try_complete_step(TestSpec *testspec, Step *step, int flags)
 {
     PGconn       *conn = conns[1 + step->session];
     fd_set        read_set;
@@ -702,6 +691,7 @@ try_complete_step(Step *step, int flags)
     int            sock = PQsocket(conn);
     int            ret;
     PGresult   *res;
+    PGnotify   *notify;
     bool        canceled = false;

     if (sock < 0)
@@ -738,7 +728,7 @@ try_complete_step(Step *step, int flags)
                 bool        waiting;

                 res = PQexecPrepared(conns[0], PREP_WAITING, 1,
-                                     &backend_pids[step->session + 1],
+                                     &backend_pid_strs[step->session + 1],
                                      NULL, NULL, 0);
                 if (PQresultStatus(res) != PGRES_TUPLES_OK ||
                     PQntuples(res) != 1)
@@ -858,6 +848,35 @@ try_complete_step(Step *step, int flags)
         PQclear(res);
     }

+    /* Report any available NOTIFY messages, too */
+    PQconsumeInput(conn);
+    while ((notify = PQnotifies(conn)) != NULL)
+    {
+        /* Try to identify which session it came from */
+        const char *sendername = NULL;
+        char        pidstring[32];
+
+        for (int i = 0; i < testspec->nsessions; i++)
+        {
+            if (notify->be_pid == backend_pids[i + 1])
+            {
+                sendername = testspec->sessions[i]->name;
+                break;
+            }
+        }
+        if (sendername == NULL)
+        {
+            /* Doesn't seem to be any test session, so show the hard way */
+            snprintf(pidstring, sizeof(pidstring), "PID %d", notify->be_pid);
+            sendername = pidstring;
+        }
+        printf("%s: NOTIFY \"%s\" with payload \"%s\" from %s\n",
+               testspec->sessions[step->session]->name,
+               notify->relname, notify->extra, sendername);
+        PQfreemem(notify);
+        PQconsumeInput(conn);
+    }
+
     return false;
 }

diff --git a/src/test/isolation/specs/async-notify.spec b/src/test/isolation/specs/async-notify.spec
index 8adad42..498e357 100644
--- a/src/test/isolation/specs/async-notify.spec
+++ b/src/test/isolation/specs/async-notify.spec
@@ -1,14 +1,68 @@
-# Verify that pg_notification_queue_usage correctly reports a non-zero result,
-# after submitting notifications while another connection is listening for
-# those notifications and waiting inside an active transaction.
+# Tests for LISTEN/NOTIFY

-session "listener"
-step "listen"    { LISTEN a; }
-step "begin"    { BEGIN; }
-teardown        { ROLLBACK; UNLISTEN *; }
+# Most of these tests use only the "notifier" session and hence exercise only
+# self-notifies, which are convenient because they minimize timing concerns.
+# Note we assume that each step is delivered to the backend as a single Query
+# message so it will run as one transaction.

 session "notifier"
-step "check"    { SELECT pg_notification_queue_usage() > 0 AS nonzero; }
-step "notify"    { SELECT count(pg_notify('a', s::text)) FROM generate_series(1, 1000) s; }
+step "listenc"    { LISTEN c1; LISTEN c2; }
+step "notify1"    { NOTIFY c1; }
+step "notify2"    { NOTIFY c2, 'payload'; }
+step "notify3"    { NOTIFY c3, 'payload3'; }  # not listening to c3
+step "notifyd"    { NOTIFY c2, 'payload'; NOTIFY c1; NOTIFY "c2", 'payload'; }
+step "notifys1"    {
+    BEGIN;
+    NOTIFY c1, 'payload1'; NOTIFY "c2", 'payload2';
+    NOTIFY c1, 'payload1'; NOTIFY "c2", 'payload2';
+    SAVEPOINT s1;
+    NOTIFY c1, 'payload1'; NOTIFY "c2", 'payload2';
+    NOTIFY c1, 'payload1s'; NOTIFY "c2", 'payload2s';
+    NOTIFY c1, 'payload1'; NOTIFY "c2", 'payload2';
+    NOTIFY c1, 'payload1s'; NOTIFY "c2", 'payload2s';
+    RELEASE SAVEPOINT s1;
+    SAVEPOINT s2;
+    NOTIFY c1, 'rpayload1'; NOTIFY "c2", 'rpayload2';
+    NOTIFY c1, 'rpayload1s'; NOTIFY "c2", 'rpayload2s';
+    NOTIFY c1, 'rpayload1'; NOTIFY "c2", 'rpayload2';
+    NOTIFY c1, 'rpayload1s'; NOTIFY "c2", 'rpayload2s';
+    ROLLBACK TO SAVEPOINT s2;
+    COMMIT;
+}
+step "usage"    { SELECT pg_notification_queue_usage() > 0 AS nonzero; }
+step "bignotify"    { SELECT count(pg_notify('c1', s::text)) FROM generate_series(1, 1000) s; }
+teardown        { UNLISTEN *; }
+
+# The listener session is used for cross-backend notify checks.
+
+session "listener"
+step "llisten"    { LISTEN c1; LISTEN c2; }
+step "lcheck"    { SELECT 1 AS x; }
+step "lbegin"    { BEGIN; }
+teardown        { UNLISTEN *; }
+
+
+# Trivial cases.
+permutation "listenc" "notify1" "notify2" "notify3"
+
+# Check simple and less-simple deduplication.
+permutation "listenc" "notifyd" "notifys1"
+
+# Cross-backend notification delivery.  We use a "select 1" to force the
+# listener session to check for notifies.  In principle we could just wait
+# for delivery, but that would require extra support in isolationtester
+# and might have portability-of-timing issues.
+permutation "llisten" "notify1" "notify2" "notify3" "lcheck"
+
+# Again, with local delivery too.
+permutation "listenc" "llisten" "notify1" "notify2" "notify3" "lcheck"
+
+# Verify that pg_notification_queue_usage correctly reports a non-zero result,
+# after submitting notifications while another connection is listening for
+# those notifications and waiting inside an active transaction.  We have to
+# fill a page of the notify SLRU to make this happen, which is a good deal
+# of traffic.  To not bloat the expected output, we intentionally don't
+# commit the listener's transaction, so that it never reports these events.
+# Hence, this should be the last test in this script.

-permutation "listen" "begin" "check" "notify" "check"
+permutation "llisten" "lbegin" "usage" "bignotify" "usage"

Re: Testing LISTEN/NOTIFY more effectively

От
Tom Lane
Дата:
Andres Freund <andres@anarazel.de> writes:
> On 2019-07-27 15:39:44 -0400, Tom Lane wrote:
>> Unfortunately, I just found out that on a slow enough machine
>> (prairiedog's host) there *is* some variation in when that test's
>> notices come out.  I am unsure whether that's to be expected or
>> whether there's something wrong there

> Hm. Any chance you could show the diff? I don't immediately see why.

Sure.  If I remove the client_min_messages hack from HEAD, then on
my dev workstation I get the attached test diff; that reproduces
quite reliably on a couple of machines.  However, running that
diff on prairiedog's host gets the failure attached second more
often than not.  (Sometimes it will pass.)

            regards, tom lane

diff --git a/src/test/isolation/expected/insert-conflict-specconflict.out
b/src/test/isolation/expected/insert-conflict-specconflict.out
index 5726bdb..20cc421 100644
--- a/src/test/isolation/expected/insert-conflict-specconflict.out
+++ b/src/test/isolation/expected/insert-conflict-specconflict.out
@@ -13,7 +13,11 @@ pg_advisory_locksess           lock
 step controller_show: SELECT * FROM upserttest;
 key            data

+s1: NOTICE:  called for k1
+s1: NOTICE:  blocking 3
 step s1_upsert: INSERT INTO upserttest(key, data) VALUES('k1', 'inserted s1') ON CONFLICT (blurt_and_lock(key)) DO
UPDATESET data = upserttest.data || ' with conflict update s1'; <waiting ...> 
+s2: NOTICE:  called for k1
+s2: NOTICE:  blocking 3
 step s2_upsert: INSERT INTO upserttest(key, data) VALUES('k1', 'inserted s2') ON CONFLICT (blurt_and_lock(key)) DO
UPDATESET data = upserttest.data || ' with conflict update s2'; <waiting ...> 
 step controller_show: SELECT * FROM upserttest;
 key            data
@@ -30,10 +34,14 @@ step controller_unlock_1_3: SELECT pg_advisory_unlock(1, 3);
 pg_advisory_unlock

 t
+s1: NOTICE:  called for k1
+s1: NOTICE:  blocking 2
 step controller_unlock_2_3: SELECT pg_advisory_unlock(2, 3);
 pg_advisory_unlock

 t
+s2: NOTICE:  called for k1
+s2: NOTICE:  blocking 2
 step controller_show: SELECT * FROM upserttest;
 key            data

@@ -50,6 +58,10 @@ step controller_unlock_1_2: SELECT pg_advisory_unlock(1, 2);
 pg_advisory_unlock

 t
+s1: NOTICE:  called for k1
+s1: NOTICE:  blocking 2
+s1: NOTICE:  called for k1
+s1: NOTICE:  blocking 2
 step s1_upsert: <... completed>
 step controller_show: SELECT * FROM upserttest;
 key            data
@@ -69,7 +81,11 @@ pg_advisory_locksess           lock
 step controller_show: SELECT * FROM upserttest;
 key            data

+s1: NOTICE:  called for k1
+s1: NOTICE:  blocking 3
 step s1_upsert: INSERT INTO upserttest(key, data) VALUES('k1', 'inserted s1') ON CONFLICT (blurt_and_lock(key)) DO
UPDATESET data = upserttest.data || ' with conflict update s1'; <waiting ...> 
+s2: NOTICE:  called for k1
+s2: NOTICE:  blocking 3
 step s2_upsert: INSERT INTO upserttest(key, data) VALUES('k1', 'inserted s2') ON CONFLICT (blurt_and_lock(key)) DO
UPDATESET data = upserttest.data || ' with conflict update s2'; <waiting ...> 
 step controller_show: SELECT * FROM upserttest;
 key            data
@@ -86,10 +102,14 @@ step controller_unlock_1_3: SELECT pg_advisory_unlock(1, 3);
 pg_advisory_unlock

 t
+s1: NOTICE:  called for k1
+s1: NOTICE:  blocking 2
 step controller_unlock_2_3: SELECT pg_advisory_unlock(2, 3);
 pg_advisory_unlock

 t
+s2: NOTICE:  called for k1
+s2: NOTICE:  blocking 2
 step controller_show: SELECT * FROM upserttest;
 key            data

@@ -106,6 +126,10 @@ step controller_unlock_2_2: SELECT pg_advisory_unlock(2, 2);
 pg_advisory_unlock

 t
+s2: NOTICE:  called for k1
+s2: NOTICE:  blocking 2
+s2: NOTICE:  called for k1
+s2: NOTICE:  blocking 2
 step s2_upsert: <... completed>
 step controller_show: SELECT * FROM upserttest;
 key            data
@@ -127,7 +151,11 @@ key            data

 step s1_begin: BEGIN;
 step s2_begin: BEGIN;
+s1: NOTICE:  called for k1
+s1: NOTICE:  blocking 3
 step s1_upsert: INSERT INTO upserttest(key, data) VALUES('k1', 'inserted s1') ON CONFLICT (blurt_and_lock(key)) DO
UPDATESET data = upserttest.data || ' with conflict update s1'; <waiting ...> 
+s2: NOTICE:  called for k1
+s2: NOTICE:  blocking 3
 step s2_upsert: INSERT INTO upserttest(key, data) VALUES('k1', 'inserted s2') ON CONFLICT (blurt_and_lock(key)) DO
UPDATESET data = upserttest.data || ' with conflict update s2'; <waiting ...> 
 step controller_show: SELECT * FROM upserttest;
 key            data
@@ -144,10 +172,14 @@ step controller_unlock_1_3: SELECT pg_advisory_unlock(1, 3);
 pg_advisory_unlock

 t
+s1: NOTICE:  called for k1
+s1: NOTICE:  blocking 2
 step controller_unlock_2_3: SELECT pg_advisory_unlock(2, 3);
 pg_advisory_unlock

 t
+s2: NOTICE:  called for k1
+s2: NOTICE:  blocking 2
 step controller_show: SELECT * FROM upserttest;
 key            data

@@ -163,10 +195,16 @@ step controller_unlock_2_2: SELECT pg_advisory_unlock(2, 2);
 pg_advisory_unlock

 t
+s2: NOTICE:  called for k1
+s2: NOTICE:  blocking 2
+s2: NOTICE:  called for k1
+s2: NOTICE:  blocking 2
 step controller_show: SELECT * FROM upserttest;
 key            data

 step s1_commit: COMMIT;
+s2: NOTICE:  called for k1
+s2: NOTICE:  blocking 2
 step s2_upsert: <... completed>
 step controller_show: SELECT * FROM upserttest;
 key            data
diff --git a/src/test/isolation/specs/insert-conflict-specconflict.spec
b/src/test/isolation/specs/insert-conflict-specconflict.spec
index 2d16fae..3a70484 100644
--- a/src/test/isolation/specs/insert-conflict-specconflict.spec
+++ b/src/test/isolation/specs/insert-conflict-specconflict.spec
@@ -51,9 +51,6 @@ session "s1"
 setup
 {
   SET default_transaction_isolation = 'read committed';
-  -- on very slow machines, the notices come out in unpredictable order,
-  -- so hide them
-  SET client_min_messages = warning;
   SET spec.session = 1;
 }
 step "s1_begin"  { BEGIN; }
@@ -64,9 +61,6 @@ session "s2"
 setup
 {
   SET default_transaction_isolation = 'read committed';
-  -- on very slow machines, the notices come out in unpredictable order,
-  -- so hide them
-  SET client_min_messages = warning;
   SET spec.session = 2;
 }
 step "s2_begin"  { BEGIN; }
diff -U3 /Users/tgl/pgsql/src/test/isolation/expected/insert-conflict-specconflict.out
/Users/tgl/pgsql/src/test/isolation/output_iso/results/insert-conflict-specconflict.out
--- /Users/tgl/pgsql/src/test/isolation/expected/insert-conflict-specconflict.out    2019-07-27 18:28:49.000000000
-0400
+++ /Users/tgl/pgsql/src/test/isolation/output_iso/results/insert-conflict-specconflict.out    2019-07-27
18:41:51.000000000-0400 
@@ -13,15 +13,15 @@
 step controller_show: SELECT * FROM upserttest;
 key            data

-s1: NOTICE:  called for k1
-s1: NOTICE:  blocking 3
 step s1_upsert: INSERT INTO upserttest(key, data) VALUES('k1', 'inserted s1') ON CONFLICT (blurt_and_lock(key)) DO
UPDATESET data = upserttest.data || ' with conflict update s1'; <waiting ...> 
-s2: NOTICE:  called for k1
-s2: NOTICE:  blocking 3
 step s2_upsert: INSERT INTO upserttest(key, data) VALUES('k1', 'inserted s2') ON CONFLICT (blurt_and_lock(key)) DO
UPDATESET data = upserttest.data || ' with conflict update s2'; <waiting ...> 
+s1: NOTICE:  called for k1
+s1: NOTICE:  blocking 3
 step controller_show: SELECT * FROM upserttest;
 key            data

+s2: NOTICE:  called for k1
+s2: NOTICE:  blocking 3
 step controller_unlock_1_1: SELECT pg_advisory_unlock(1, 1);
 pg_advisory_unlock


Re: Testing LISTEN/NOTIFY more effectively

От
Andres Freund
Дата:
Hi,

On 2019-07-27 18:20:52 -0400, Tom Lane wrote:
> diff --git a/src/test/isolation/isolationtester.c b/src/test/isolation/isolationtester.c
> index 6ab19b1..98e5bf2 100644
> --- a/src/test/isolation/isolationtester.c
> +++ b/src/test/isolation/isolationtester.c
> @@ -23,10 +23,12 @@
>  
>  /*
>   * conns[0] is the global setup, teardown, and watchdog connection.  Additional
> - * connections represent spec-defined sessions.
> + * connections represent spec-defined sessions.  We also track the backend
> + * PID, in numeric and string formats, for each connection.
>   */
>  static PGconn **conns = NULL;
> -static const char **backend_pids = NULL;
> +static int *backend_pids = NULL;
> +static const char **backend_pid_strs = NULL;
>  static int    nconns = 0;

Hm, a bit sad to have both of those around. Not worth getting bothered
about memory wise, but it does irk me somewhat.


> @@ -187,26 +191,9 @@ main(int argc, char **argv)
>                                   blackholeNoticeProcessor,
>                                   NULL);
>  
> -        /* Get the backend pid for lock wait checking. */
> -        res = PQexec(conns[i], "SELECT pg_catalog.pg_backend_pid()");
> -        if (PQresultStatus(res) == PGRES_TUPLES_OK)
> -        {
> -            if (PQntuples(res) == 1 && PQnfields(res) == 1)
> -                backend_pids[i] = pg_strdup(PQgetvalue(res, 0, 0));
> -            else
> -            {
> -                fprintf(stderr, "backend pid query returned %d rows and %d columns, expected 1 row and 1 column",
> -                        PQntuples(res), PQnfields(res));
> -                exit(1);
> -            }
> -        }
> -        else
> -        {
> -            fprintf(stderr, "backend pid query failed: %s",
> -                    PQerrorMessage(conns[i]));
> -            exit(1);
> -        }
> -        PQclear(res);
> +        /* Save each connection's backend PID for subsequent use. */
> +        backend_pids[i] = PQbackendPID(conns[i]);
> +        backend_pid_strs[i] = psprintf("%d", backend_pids[i]);

Heh.


> @@ -738,7 +728,7 @@ try_complete_step(Step *step, int flags)
>                  bool        waiting;
>  
>                  res = PQexecPrepared(conns[0], PREP_WAITING, 1,
> -                                     &backend_pids[step->session + 1],
> +                                     &backend_pid_strs[step->session + 1],
>                                       NULL, NULL, 0);
>                  if (PQresultStatus(res) != PGRES_TUPLES_OK ||
>                      PQntuples(res) != 1)

We could of course just send the pids in binary ;). No, not worth it
just to avoid a small redundant array ;)


> +    /* Report any available NOTIFY messages, too */
> +    PQconsumeInput(conn);
> +    while ((notify = PQnotifies(conn)) != NULL)
> +    {

Hm. I wonder if all that's happening with prairedog is that the notice
is sent a bit later. I think that could e.g. conceivably happen because
it TCP_NODELAY isn't supported on prariedog? Or just because the machine
is very slow?

The diff you showed with the reordering afaict only reordered the NOTIFY
around statements that are marked as <waiting ...>. As the waiting
detection is done over a separate connection, there's afaict no
guarantee that we see all notices/notifies that occurred before the
query started blocking.  It's possible we could make this practically
robust enough by checking for notice/notifies on the blocked connection
just before printing out the <waiting ...>?  That still leaves the
potential issue that the different backend connection deliver data out
of order, but that seems not very likely?


Greetings,

Andres Freund



Re: Testing LISTEN/NOTIFY more effectively

От
Tom Lane
Дата:
Andres Freund <andres@anarazel.de> writes:
> We could of course just send the pids in binary ;). No, not worth it
> just to avoid a small redundant array ;)

IIRC, we'd have to do htonl on them, so we'd still end up with
two representations ...

> Hm. I wonder if all that's happening with prairedog is that the notice
> is sent a bit later. I think that could e.g. conceivably happen because
> it TCP_NODELAY isn't supported on prariedog? Or just because the machine
> is very slow?

The notices (not notifies) are coming out in the opposite order from
expected.  I haven't really thought hard about what's causing that;
it seems odd, because isolationtester isn't supposed to give up waiting
for a session until it's visibly blocked according to pg_locks.  Maybe
it needs to recheck for incoming data once more after seeing that?

            regards, tom lane



Re: Testing LISTEN/NOTIFY more effectively

От
Tom Lane
Дата:
I wrote:
> Andres Freund <andres@anarazel.de> writes:
>> Hm. I wonder if all that's happening with prairedog is that the notice
>> is sent a bit later. I think that could e.g. conceivably happen because
>> it TCP_NODELAY isn't supported on prariedog? Or just because the machine
>> is very slow?

> The notices (not notifies) are coming out in the opposite order from
> expected.  I haven't really thought hard about what's causing that;
> it seems odd, because isolationtester isn't supposed to give up waiting
> for a session until it's visibly blocked according to pg_locks.  Maybe
> it needs to recheck for incoming data once more after seeing that?

Ah-hah, that seems to be the answer.  With the attached patch I'm
getting reliable-seeming passes on prairiedog.

            regards, tom lane

diff --git a/src/test/isolation/isolationtester.c b/src/test/isolation/isolationtester.c
index 6ab19b1..e97fef1 100644
*** a/src/test/isolation/isolationtester.c
--- b/src/test/isolation/isolationtester.c
*************** try_complete_step(Step *step, int flags)
*** 752,757 ****
--- 752,777 ----

                  if (waiting)    /* waiting to acquire a lock */
                  {
+                     /*
+                      * Since it takes time to perform the lock-check query,
+                      * some data --- notably, NOTICE messages --- might have
+                      * arrived since we looked.  We should do PQconsumeInput
+                      * to process any such messages, and we might as well then
+                      * check PQisBusy, though it's unlikely to succeed.
+                      */
+                     if (!PQconsumeInput(conn))
+                     {
+                         fprintf(stderr, "PQconsumeInput failed: %s\n",
+                                 PQerrorMessage(conn));
+                         exit(1);
+                     }
+                     if (!PQisBusy(conn))
+                         break;
+
+                     /*
+                      * conn is still busy, so conclude that the step really is
+                      * waiting.
+                      */
                      if (!(flags & STEP_RETRY))
                          printf("step %s: %s <waiting ...>\n",
                                 step->name, step->sql);

Re: Testing LISTEN/NOTIFY more effectively

От
Andres Freund
Дата:
Hi,

On 2019-07-27 19:27:17 -0400, Tom Lane wrote:
> Andres Freund <andres@anarazel.de> writes:
> > We could of course just send the pids in binary ;). No, not worth it
> > just to avoid a small redundant array ;)
> 
> IIRC, we'd have to do htonl on them, so we'd still end up with
> two representations ...

Yea. Although that'd could just be done in a local variable. Anyway,
it's obviously not important.


> > Hm. I wonder if all that's happening with prairedog is that the notice
> > is sent a bit later. I think that could e.g. conceivably happen because
> > it TCP_NODELAY isn't supported on prariedog? Or just because the machine
> > is very slow?
> 
> The notices (not notifies) are coming out in the opposite order from
> expected.  I haven't really thought hard about what's causing that;
> it seems odd, because isolationtester isn't supposed to give up waiting
> for a session until it's visibly blocked according to pg_locks.  Maybe
> it needs to recheck for incoming data once more after seeing that?

Yea, that's precisely what I was trying to refer to / suggesting. What I
think is happening is that both queries get sent to the server, we
PQisBusy();select() and figure out they're not done yet. On most
machines the raise NOTICE will have been processed by that time, after
it's a trivial query. But on prariedog (and I suspect even more likely
on valgrind / clobber cache animals), they're not that far yet. So we
send the blocking query, until we've seen that it blocks. But there's no
interlock guaranteeing that we'll have seen the notices before the
*other* connection has detected us blocking.  As the blocking query is
more complex to plan and execute, that window isn't that small.

Polling for notices on the blocked connection before printing anything
ought to practically be reliable. Theoretically I think it still allows
for some reordering, e.g. because there was packet loss on one, but not
the other connection.

Greetings,

Andres Freund



Re: Testing LISTEN/NOTIFY more effectively

От
Tom Lane
Дата:
Andres Freund <andres@anarazel.de> writes:
> Polling for notices on the blocked connection before printing anything
> ought to practically be reliable. Theoretically I think it still allows
> for some reordering, e.g. because there was packet loss on one, but not
> the other connection.

As long as it's a local connection, packet loss shouldn't be a problem
;-).  I'm slightly more worried about the case of more than one bufferful
of NOTICE messages: calling PQconsumeInput isn't entirely guaranteed to
absorb *all* available input.  But for the cases we actually need to
deal with, I think probably the patch as I sent it is OK.  We could
complicate matters by going around the loop extra time(s) to verify
that select() thinks no data is waiting, but I doubt it's worth the
complexity.

            regards, tom lane



Re: Testing LISTEN/NOTIFY more effectively

От
Andres Freund
Дата:
Hi,

On 2019-07-27 20:02:13 -0400, Tom Lane wrote:
> Andres Freund <andres@anarazel.de> writes:
> I'm slightly more worried about the case of more than one bufferful
> of NOTICE messages: calling PQconsumeInput isn't entirely guaranteed to
> absorb *all* available input.  But for the cases we actually need to
> deal with, I think probably the patch as I sent it is OK.  We could
> complicate matters by going around the loop extra time(s) to verify
> that select() thinks no data is waiting, but I doubt it's worth the
> complexity.

It'd just be one continue; right? Except that we don't know if
PQconsumeInput() actually did anything... So we'd need to do something
like executing a select and only call PQconsumeInput() if the select
signals that there's data? And then always retry? Yea, that seems too
complicated.

Kinda annoying that we don't expose pqReadData()'s return value anywhere
that I can see. Not so much for this, but in general. Travelling back
into the past, ISTM, PQconsumeInput() should have returned a different
return code if either pqReadData() or pqFlush() did anything.

I wonder if there aren't similar dangers around the notify handling. In
your patch we don't print them particularly eagerly. Doesn't that also
open us up to timing concerns? In particular, for notifies sent out
while idle, we might print them together with the *last* command
executed - as far as I can tell, if they arrive before the
PQconsumeInput(), we'll process them all in the PQisBusy() call at the
top of try_complete_step()'s loop? Am I missing some interlock here?

Greetings,

Andres Freund



Re: Testing LISTEN/NOTIFY more effectively

От
Tom Lane
Дата:
Andres Freund <andres@anarazel.de> writes:
> I wonder if there aren't similar dangers around the notify handling. In
> your patch we don't print them particularly eagerly. Doesn't that also
> open us up to timing concerns?

I think probably not, because of the backend-side restrictions on when
notify messages will be sent.  The corresponding case for the NOTICE
bug we just fixed would be if a backend sent a NOTIFY before blocking;
but it can't do that internally to a transaction, and anyway the proposed
test script isn't doing anything that tricky.

I did spend some time thinking about how isolationtester might report
notifys that are sent spontaneously (without any "triggering" query)
but I didn't feel that that was worth messing with.  We'd have to
have the program checking all the connections not just the one that's
running what it thinks is the currently active step.

We might be approaching a time where it's worth scrapping the
isolationtester logic and starting over.  I'm not volunteering though.

            regards, tom lane



Re: Testing LISTEN/NOTIFY more effectively

От
Tom Lane
Дата:
Andres Freund <andres@anarazel.de> writes:
> On 2019-07-27 12:46:51 -0400, Tom Lane wrote:
>> 2. Change psql so that there's a way to get NOTIFY messages without
>> the sending PID.  For testing purposes, it'd be sufficient to know
>> whether the sending PID is our own backend's PID or not.  This idea
>> is not horrible, and it might even be useful for outside purposes
>> if we made it flexible enough; which leads to thoughts like allowing
>> the psql user to set a format-style string, similar to the PROMPT
>> strings but with escapes for channel name, payload, etc.  I foresee
>> bikeshedding, but we could probably come to an agreement on a feature
>> like that.

> I was wondering about just tying it to VERBOSITY. But that'd not allow
> us to see whether our backend was the sender. I'm mildly inclined to
> think that that might still be a good idea, even if we mostly go with
> 3) - some basic plain regression test coverage of actually receiving
> notifies would be good.

BTW, as far as that goes, do you think we could get away with changing
psql to print "from self" instead of "from PID n" when it's a self-notify?
That would be enough to make the output stable for cases that we'd be
able to check in the core test infrastructure.

So far as the backend is concerned, doing anything there is redundant
with the isolation tests I just committed --- but it would allow psql's
own notify code to be covered, so maybe it's worth the trouble.

            regards, tom lane