diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index e958dbc6af..7c1771eae7 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -2038,6 +2038,14 @@ CommitTransaction(void) /* close large objects before lower-level cleanup */ AtEOXact_LargeObject(true); + /* + * Insert notifications sent by NOTIFY commands into the queue. This + * should be late in the pre-commit sequence to minimize time spent + * holding the notify-insertion lock. However, this could result in + * creating a snapshot, so we must do it before serializable cleanup. + */ + PreCommit_Notify(); + /* * Mark serializable transaction as complete for predicate locking * purposes. This should be done as late as we can put it and still allow @@ -2045,13 +2053,6 @@ CommitTransaction(void) */ PreCommit_CheckForSerializationFailure(); - /* - * Insert notifications sent by NOTIFY commands into the queue. This - * should be late in the pre-commit sequence to minimize time spent - * holding the notify-insertion lock. - */ - PreCommit_Notify(); - /* Prevent cancel/die interrupt while cleaning up */ HOLD_INTERRUPTS(); @@ -2266,6 +2267,8 @@ PrepareTransaction(void) /* close large objects before lower-level cleanup */ AtEOXact_LargeObject(true); + /* NOTIFY requires no work at this point */ + /* * Mark serializable transaction as complete for predicate locking * purposes. This should be done as late as we can put it and still allow @@ -2273,8 +2276,6 @@ PrepareTransaction(void) */ PreCommit_CheckForSerializationFailure(); - /* NOTIFY will be handled below */ - /* * Don't allow PREPARE TRANSACTION if we've accessed a temporary table in * this transaction. Having the prepared xact hold locks on another diff --git a/src/test/isolation/expected/async-notify.out b/src/test/isolation/expected/async-notify.out index 60ba50658d..3a9bf3c310 100644 --- a/src/test/isolation/expected/async-notify.out +++ b/src/test/isolation/expected/async-notify.out @@ -1,4 +1,4 @@ -Parsed test spec with 2 sessions +Parsed test spec with 3 sessions starting permutation: listenc notify1 notify2 notify3 notifyf step listenc: LISTEN c1; LISTEN c2; @@ -85,6 +85,17 @@ listener: NOTIFY "c1" with payload "" from notifier listener: NOTIFY "c2" with payload "payload" from notifier listener: NOTIFY "c2" with payload "" from notifier +starting permutation: l2listen l2begin notify1 lbegins llisten lcommit l2commit l2stop +step l2listen: LISTEN c1; +step l2begin: BEGIN; +step notify1: NOTIFY c1; +step lbegins: BEGIN ISOLATION LEVEL SERIALIZABLE; +step llisten: LISTEN c1; LISTEN c2; +step lcommit: COMMIT; +step l2commit: COMMIT; +listener2: NOTIFY "c1" with payload "" from notifier +step l2stop: UNLISTEN *; + starting permutation: llisten lbegin usage bignotify usage step llisten: LISTEN c1; LISTEN c2; step lbegin: BEGIN; diff --git a/src/test/isolation/specs/async-notify.spec b/src/test/isolation/specs/async-notify.spec index daf7bef903..a7b2600d25 100644 --- a/src/test/isolation/specs/async-notify.spec +++ b/src/test/isolation/specs/async-notify.spec @@ -41,8 +41,18 @@ session "listener" step "llisten" { LISTEN c1; LISTEN c2; } step "lcheck" { SELECT 1 AS x; } step "lbegin" { BEGIN; } +step "lbegins" { BEGIN ISOLATION LEVEL SERIALIZABLE; } +step "lcommit" { COMMIT; } teardown { UNLISTEN *; } +# In some tests we need a second listener, just to block the queue. + +session "listener2" +step "l2listen" { LISTEN c1; } +step "l2begin" { BEGIN; } +step "l2commit" { COMMIT; } +step "l2stop" { UNLISTEN *; } + # Trivial cases. permutation "listenc" "notify1" "notify2" "notify3" "notifyf" @@ -59,6 +69,10 @@ permutation "llisten" "notify1" "notify2" "notify3" "notifyf" "lcheck" # Again, with local delivery too. permutation "listenc" "llisten" "notify1" "notify2" "notify3" "notifyf" "lcheck" +# Check for bug when initial listen is only action in a serializable xact, +# and notify queue is not empty +permutation "l2listen" "l2begin" "notify1" "lbegins" "llisten" "lcommit" "l2commit" "l2stop" + # 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