Skip to content

Commit 071d656

Browse files
committed
Fix Database#listen in postgres adapter to ignore all notifications after UNLISTEN
Previously, there was a race condition. If not all notifications received between the LISTEN and UNLISTEN were handled, subsequent notifications would stick around until the next time listen (or wait_for_notify) was called, and returned at that point, which could be for the previous channel and not the current channel. Use a loop to ensure that all notifications received have been ignored after the UNLISTEN, so the next LISTEN starts with a clean slate.
1 parent 43889b9 commit 071d656

File tree

3 files changed

+14
-0
lines changed

3 files changed

+14
-0
lines changed

CHANGELOG

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
=== master
22

3+
* Fix Database#listen in postgres adapter to ignore all notifications after UNLISTEN (jeremyevans)
4+
35
* Make Model.any? forward to the dataset, to work better with the any_not_empty extension (jeremyevans) (#2337)
46

57
* Make the rcte_tree and split_values plugins work together (jeremyevans)

lib/sequel/adapters/postgres.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,7 @@ def listen(channels, opts=OPTS, &block)
516516
end
517517
ensure
518518
conn.execute("UNLISTEN *")
519+
true while conn.wait_for_notify(0)
519520
end
520521
end
521522
end

spec/adapters/postgres_spec.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3312,6 +3312,17 @@ def c.get_last_result; end
33123312
proc{@db.listen('foo', :loop=>true)}.must_raise Sequel::Error
33133313
end
33143314

3315+
it "should clear notifications received after UNLISTEN" do
3316+
DB.listen('foo', :after_listen=>proc{2.times{@db.notify('foo', :payload=>'bar')}}) do |x, _, y|
3317+
x.must_equal 'foo'
3318+
y.must_equal 'bar'
3319+
end.must_equal 'foo'
3320+
DB.listen('quux', :after_listen=>proc{@db.notify('quux', :payload=>'baz')}) do |x, _, y|
3321+
x.must_equal 'quux'
3322+
y.must_equal 'baz'
3323+
end.must_equal 'quux'
3324+
end
3325+
33153326
it "should accept a :timeout option in listen" do
33163327
@db.listen('foo2', :timeout=>0.001).must_be_nil
33173328
called = false

0 commit comments

Comments
 (0)