Skip to content

Commit 47af990

Browse files
Run Bunny::Channel#on_error in a separate thread
This callback already runs on a thread that's different from the application thread, so this behavior change should not affect any apps. On the upside, this allows the callback use `Bunny::Session` methods, including protocol operation ones, without blocking the I/O loop. References ruby-amqp/hutch#414
1 parent feb133a commit 47af990

3 files changed

Lines changed: 28 additions & 2 deletions

File tree

ChangeLog.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
## Changes between Bunny 3.0.0 and 3.1.0 (unrelesed)
22

3-
(no changes yet)
3+
### `on_error` Callback Runs in a Separate Thread
4+
5+
`Channel#on_error` callbacks are now invoked in a separate thread
6+
when triggered by the reader loop. This allows callbacks to perform
7+
blocking operations such as `Channel#reopen` without deadlocking.
48

59

610
## Changes between Bunny 2.24.0 and 3.0.0 (March 31, 2026)

lib/bunny/channel.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2250,7 +2250,9 @@ def handle_method(method)
22502250

22512251
# basic.ack, basic.reject, basic.nack. MK.
22522252
if channel_level_exception_after_operation_that_has_no_response?(method)
2253-
@on_error.call(self, method) if @on_error
2253+
# Runs outside the reader loop so that the callback can perform
2254+
# blocking operations such as channel.reopen.
2255+
Thread.new { @on_error.call(self, method) } if @on_error
22542256
else
22552257
@last_channel_error = instantiate_channel_level_exception(method)
22562258
@continuations.push(method)

spec/higher_level_api/integration/channel_reopen_spec.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
require "spec_helper"
2+
require "bunny/concurrent/condition"
23

34
describe Bunny::Channel, "#reopen" do
45
let(:connection) do
@@ -11,6 +12,25 @@
1112
connection.close if connection.open?
1213
end
1314

15+
it "can reopen a channel directly inside on_error" do
16+
ch = connection.create_channel
17+
done = Bunny::Concurrent::Condition.new
18+
19+
ch.on_error do |closed_ch, amq_close|
20+
closed_ch.reopen
21+
done.notify
22+
end
23+
24+
ch.ack(82, false)
25+
done.wait
26+
expect(ch).to be_open
27+
28+
q = ch.queue("bunny.test.channel-reopen.on-error.#{rand}", exclusive: true)
29+
ch.default_exchange.publish("hello", routing_key: q.name)
30+
sleep 0.25
31+
expect(q.message_count).to eq 1
32+
end
33+
1434
it "reopens a channel after a server-initiated closure" do
1535
ch = connection.create_channel
1636
q = ch.queue("bunny.test.channel-reopen.#{rand}", exclusive: true)

0 commit comments

Comments
 (0)