Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## 0.26.1 (2026-10-01)

### Fixed

- Resetting a publisher channel pool while a closed channel is checked out no longer raises `Bunny::ChannelAlreadyClosed` on checkin. Previously this masked the original error (e.g. `PublishConfirmationTimeout`) and caused it to be retried as a connection error, which could duplicate a message.

## 0.26.0 (2026-06-01)

- Upgrade `connection_pool` dependency to `~> 3.0`
Expand Down
4 changes: 2 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
ears (0.26.0)
ears (0.26.1)
bunny (>= 3.0.0)
connection_pool (~> 3.0)
json (>= 2.9.0)
Expand Down Expand Up @@ -102,7 +102,7 @@ CHECKSUMS
bunny (3.3.0) sha256=85ef5562ec2b24f542e8fad69c4302d30d966fb79ff803dc9d0c398a4054672a
connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
ears (0.26.0)
ears (0.26.1)
json (2.21.2) sha256=1f1d3b7cf2b3ba1a69beca0bb6db13d5438b80bff3cd54cdaaa620b9b07c1c6a
language_server-protocol (3.17.0.6) sha256=5ef2c0c138f8267e1bc631d3328347d354f96724b0af22f2c79516120443b7f0
lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
Expand Down
12 changes: 9 additions & 3 deletions lib/ears/publisher_channel_pool.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ def reset!
@confirms_pool = nil
@creator_pid = nil

std_pool&.shutdown(&:close)
cnf_pool&.shutdown(&:close)
std_pool&.shutdown { |channel| close_channel(channel) }
cnf_pool&.shutdown { |channel| close_channel(channel) }
nil
end

Expand All @@ -47,7 +47,7 @@ def reset_confirms_pool!
cnf_pool = @confirms_pool
@confirms_pool = nil

cnf_pool&.shutdown(&:close)
cnf_pool&.shutdown { |channel| close_channel(channel) }
nil
end

Expand Down Expand Up @@ -112,6 +112,12 @@ def validate_channel_health!(channel, confirms)
'Channel is closed'
end

def close_channel(channel)
channel.close
rescue Bunny::ChannelAlreadyClosed
nil
end

def cleanup_closed_channel(channel)
return unless channel.respond_to?(:close)

Expand Down
2 changes: 1 addition & 1 deletion lib/ears/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module Ears
VERSION = '0.26.0'
VERSION = '0.26.1'
end
36 changes: 36 additions & 0 deletions spec/ears/publisher_channel_pool_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,42 @@
end
end

context 'when the pool is reset while its channel is checked out and closed' do
let(:mock_channel) do
instance_double(Bunny::Channel, confirm_select: nil)
end

before do
allow(mock_channel).to receive(:open?).and_return(true, false)
allow(mock_channel).to receive(:close).and_raise(
Bunny::ChannelAlreadyClosed.new(
'cannot use a closed channel!',
mock_channel,
),
)
end

it 'returns the result of the block' do
result =
described_class.with_channel(confirms: true) do
described_class.reset_confirms_pool!
:published
end

expect(result).to eq(:published)
end

it 'raises the error from the block' do
expect {
described_class.with_channel(confirms: true) do
described_class.reset_confirms_pool!
raise Ears::PublishConfirmationTimeout,
'Confirmation timeout after 5.0s'
end
}.to raise_error(Ears::PublishConfirmationTimeout)
end
end

context 'with confirms: false (default)' do
it 'yields a channel from the standard pool' do
allow(ConnectionPool).to receive(:new).and_return(mock_standard_pool)
Expand Down
Loading