Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 6b802cf

Browse files
committed
Avoid assertion failure with LISTEN in a serializable transaction.
If LISTEN is the only action in a serializable-mode transaction, and the session was not previously listening, and the notify queue is not empty, predicate.c reported an assertion failure. That happened because we'd acquire the transaction's initial snapshot during PreCommit_Notify, which was called *after* predicate.c expects any such snapshot to have been established. To fix, just swap the order of the PreCommit_Notify and PreCommit_CheckForSerializationFailure calls during CommitTransaction. This will imply holding the notify-insertion lock slightly longer, but the difference could only be meaningful in serializable mode, which is an expensive option anyway. It appears that this is just an assertion failure, with no consequences in non-assert builds. A snapshot used only to scan the notify queue could not have been involved in any serialization conflicts, so there would be nothing for PreCommit_CheckForSerializationFailure to do except assign it a prepareSeqNo and set the SXACT_FLAG_PREPARED flag. And given no conflicts, neither of those omissions affect the behavior of ReleasePredicateLocks. This admittedly once-over-lightly analysis is backed up by the lack of field reports of trouble. Per report from Mark Dilger. The bug is old, so back-patch to all supported branches; but the new test case only goes back to 9.6, for lack of adequate isolationtester infrastructure before that. Discussion: https://postgr.es/m/3ac7f397-4d5f-be8e-f354-440020675694@gmail.com Discussion: https://postgr.es/m/13881.1574557302@sss.pgh.pa.us
1 parent 1974853 commit 6b802cf

File tree

3 files changed

+36
-10
lines changed

3 files changed

+36
-10
lines changed

src/backend/access/transam/xact.c

+10-9
Original file line numberDiff line numberDiff line change
@@ -2112,6 +2112,14 @@ CommitTransaction(void)
21122112
/* close large objects before lower-level cleanup */
21132113
AtEOXact_LargeObject(true);
21142114

2115+
/*
2116+
* Insert notifications sent by NOTIFY commands into the queue. This
2117+
* should be late in the pre-commit sequence to minimize time spent
2118+
* holding the notify-insertion lock. However, this could result in
2119+
* creating a snapshot, so we must do it before serializable cleanup.
2120+
*/
2121+
PreCommit_Notify();
2122+
21152123
/*
21162124
* Mark serializable transaction as complete for predicate locking
21172125
* purposes. This should be done as late as we can put it and still allow
@@ -2122,13 +2130,6 @@ CommitTransaction(void)
21222130
if (!is_parallel_worker)
21232131
PreCommit_CheckForSerializationFailure();
21242132

2125-
/*
2126-
* Insert notifications sent by NOTIFY commands into the queue. This
2127-
* should be late in the pre-commit sequence to minimize time spent
2128-
* holding the notify-insertion lock.
2129-
*/
2130-
PreCommit_Notify();
2131-
21322133
/* Prevent cancel/die interrupt while cleaning up */
21332134
HOLD_INTERRUPTS();
21342135

@@ -2344,15 +2345,15 @@ PrepareTransaction(void)
23442345
/* close large objects before lower-level cleanup */
23452346
AtEOXact_LargeObject(true);
23462347

2348+
/* NOTIFY requires no work at this point */
2349+
23472350
/*
23482351
* Mark serializable transaction as complete for predicate locking
23492352
* purposes. This should be done as late as we can put it and still allow
23502353
* errors to be raised for failure patterns found at commit.
23512354
*/
23522355
PreCommit_CheckForSerializationFailure();
23532356

2354-
/* NOTIFY will be handled below */
2355-
23562357
/*
23572358
* Don't allow PREPARE TRANSACTION if we've accessed a temporary table in
23582359
* this transaction. Having the prepared xact hold locks on another

src/test/isolation/expected/async-notify.out

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Parsed test spec with 2 sessions
1+
Parsed test spec with 3 sessions
22

33
starting permutation: listenc notify1 notify2 notify3 notifyf
44
step listenc: LISTEN c1; LISTEN c2;
@@ -83,6 +83,17 @@ listener: NOTIFY "c1" with payload "" from notifier
8383
listener: NOTIFY "c2" with payload "payload" from notifier
8484
listener: NOTIFY "c2" with payload "" from notifier
8585

86+
starting permutation: l2listen l2begin notify1 lbegins llisten lcommit l2commit l2stop
87+
step l2listen: LISTEN c1;
88+
step l2begin: BEGIN;
89+
step notify1: NOTIFY c1;
90+
step lbegins: BEGIN ISOLATION LEVEL SERIALIZABLE;
91+
step llisten: LISTEN c1; LISTEN c2;
92+
step lcommit: COMMIT;
93+
step l2commit: COMMIT;
94+
listener2: NOTIFY "c1" with payload "" from notifier
95+
step l2stop: UNLISTEN *;
96+
8697
starting permutation: llisten lbegin usage bignotify usage
8798
step llisten: LISTEN c1; LISTEN c2;
8899
step lbegin: BEGIN;

src/test/isolation/specs/async-notify.spec

+14
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,18 @@ session "listener"
4141
step "llisten" { LISTEN c1; LISTEN c2; }
4242
step "lcheck" { SELECT 1 AS x; }
4343
step "lbegin" { BEGIN; }
44+
step "lbegins" { BEGIN ISOLATION LEVEL SERIALIZABLE; }
45+
step "lcommit" { COMMIT; }
4446
teardown { UNLISTEN *; }
4547

48+
# In some tests we need a second listener, just to block the queue.
49+
50+
session "listener2"
51+
step "l2listen" { LISTEN c1; }
52+
step "l2begin" { BEGIN; }
53+
step "l2commit" { COMMIT; }
54+
step "l2stop" { UNLISTEN *; }
55+
4656

4757
# Trivial cases.
4858
permutation "listenc" "notify1" "notify2" "notify3" "notifyf"
@@ -59,6 +69,10 @@ permutation "llisten" "notify1" "notify2" "notify3" "notifyf" "lcheck"
5969
# Again, with local delivery too.
6070
permutation "listenc" "llisten" "notify1" "notify2" "notify3" "notifyf" "lcheck"
6171

72+
# Check for bug when initial listen is only action in a serializable xact,
73+
# and notify queue is not empty
74+
permutation "l2listen" "l2begin" "notify1" "lbegins" "llisten" "lcommit" "l2commit" "l2stop"
75+
6276
# Verify that pg_notification_queue_usage correctly reports a non-zero result,
6377
# after submitting notifications while another connection is listening for
6478
# those notifications and waiting inside an active transaction. We have to

0 commit comments

Comments
 (0)