bunny 2.5.1 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8d4e4f01cf78b6c66e7f006768d58fd6a84e743e
4
- data.tar.gz: 8bcd0912b2c1b010aacd3a2ba8484faadc6f4e76
3
+ metadata.gz: 00d3ad97db0f16249b51ccf1471f6b9c19a6a57d
4
+ data.tar.gz: 7011427b335e9a4913c180310c1bd3eec7b8f34f
5
5
  SHA512:
6
- metadata.gz: 6c062301c5be89c101e9a03d98fd571870859e467f94c21cf54924b1c2555cb6a49824a1bacbf79ba23275b4ef960f3a0059f3c26466230484286be54e316402
7
- data.tar.gz: b1b6fb96de9547bfdedbb02efb4c4b18c55642a26cc16f8a4c1b96599d45ee0f567ce828a4c835701033c3cb18c2c6ccf8c5da6052c7aff2690df52c8eb3c024
6
+ metadata.gz: bc7a6e64cbee2ca138a14df95e7d84248262d1593dbd27832d9b03dbaef6b4d78df06bc5ce1777f313e3dae8f169672b1e8de8f93191e0b39b8e0e087a4898d9
7
+ data.tar.gz: fe51e2257d33a5954921cc55bb511ea8496ef2425030077894a098d4eb07efe899f58242be2f2db02e1da007d5d83add2dee03030d0bafd598c8ef61cb140255
data/.rspec CHANGED
@@ -1,3 +1 @@
1
- --color
2
- --format
3
- progress
1
+ -c -fp
@@ -1,9 +1,25 @@
1
- ## Changes between Bunny 2.5.1 and 2.5.2 (unreleased)
1
+ ## Changes between Bunny 2.5.0 and 2.6.0 (unreleased)
2
2
 
3
- No changes yet.
3
+ ### Graceful Shutdown of Consumers
4
4
 
5
+ Consumer work pool will now allow for a grace period before stopping
6
+ pool threads so that delivery processing in progress can have a chance to finish.
5
7
 
6
- ## Changes between Bunny 2.5.0 and 2.5.1 (August 15th, 2016)
8
+ GitHub issue: [#437](https://github.com/ruby-amqp/bunny/pull/437)
9
+
10
+ Contributed by Stefan Sedich.
11
+
12
+ ### `Bunny::Channel#wait_for_confirms` Now Throws When Used on a Closed Channel
13
+
14
+ GitHub issue: [#428](https://github.com/ruby-amqp/bunny/pull/428)
15
+
16
+ Contributed by Dimitar Dimitrov.
17
+
18
+ ### Race Condition Eliminated in `Bunny::Channel#wait_for_confirms`
19
+
20
+ GitHub issue: [#424](https://github.com/ruby-amqp/bunny/issues/424)
21
+
22
+ Contributed by Dimitar Dimitrov.
7
23
 
8
24
  ### More Defensive Consumer Work Pool
9
25
 
@@ -49,6 +65,8 @@ Contributed by Dimitar Dimitrov.
49
65
 
50
66
  ## Changes between Bunny 2.3.0 and 2.4.0 (June 11th, 2016)
51
67
 
68
+ **This release includes minor breaking API changes**.
69
+
52
70
  ### Unconfirmed Delivery Tag Set Reset on Network Recovery
53
71
 
54
72
  Channels will now reset their unconfirmed delivery tag set after
@@ -81,6 +99,8 @@ override a single key.
81
99
 
82
100
  ### More Predictable RABBITMQ_URL Handling
83
101
 
102
+ **This is a breaking API change**.
103
+
84
104
  `RABBITMQ_URL` no longer will be used if any other
85
105
  connection options are provided. This makes it possible
86
106
  to use `RABBITMQ_URL` for some connections and options
data/README.md CHANGED
@@ -59,7 +59,7 @@ Bunny `1.7.x` was the last version to support CRuby 1.9.3 and 1.8.7
59
59
 
60
60
  ## Supported RabbitMQ Versions
61
61
 
62
- Bunny `1.5.0` (including previews) and later versions only support RabbitMQ `3.3+`.
62
+ Bunny `1.5.0` and later versions only support RabbitMQ `3.3+`.
63
63
  Bunny `1.4.x` and earlier supports RabbitMQ 2.x and 3.x.
64
64
 
65
65
 
@@ -88,7 +88,7 @@ gem install bunny
88
88
  To use Bunny in a project managed with Bundler:
89
89
 
90
90
  ``` ruby
91
- gem "bunny", ">= 2.3.1"
91
+ gem "bunny", ">= 2.5.1"
92
92
  ```
93
93
 
94
94
 
@@ -952,7 +952,7 @@ module Bunny
952
952
  @last_basic_cancel_ok = wait_on_continuations
953
953
  end
954
954
 
955
- maybe_kill_consumer_work_pool! unless any_consumers?
955
+ @work_pool.shutdown(true) unless any_consumers?
956
956
 
957
957
  @last_basic_cancel_ok
958
958
  end
@@ -1790,11 +1790,9 @@ module Bunny
1790
1790
  @threads_waiting_on_confirms_continuations << t
1791
1791
 
1792
1792
  begin
1793
- outstanding_confirms = false
1794
- @unconfirmed_set_mutex.synchronize do
1795
- outstanding_confirms = !@unconfirmed_set.empty?
1793
+ while @unconfirmed_set_mutex.synchronize { !@unconfirmed_set.empty? }
1794
+ @confirms_continuations.poll(@connection.continuation_timeout)
1796
1795
  end
1797
- @confirms_continuations.poll(@connection.continuation_timeout) if outstanding_confirms
1798
1796
  ensure
1799
1797
  @threads_waiting_on_confirms_continuations.delete(t)
1800
1798
  end
@@ -17,9 +17,12 @@ module Bunny
17
17
  attr_reader :size
18
18
  attr_reader :abort_on_exception
19
19
 
20
- def initialize(size = 1, abort_on_exception = false)
20
+ def initialize(size = 1, abort_on_exception = false, shutdown_timeout = 60)
21
21
  @size = size
22
22
  @abort_on_exception = abort_on_exception
23
+ @shutdown_timeout = shutdown_timeout
24
+ @shutdown_mutex = ::Mutex.new
25
+ @shutdown_conditional = ::ConditionVariable.new
23
26
  @queue = ::Queue.new
24
27
  @paused = false
25
28
  end
@@ -53,7 +56,7 @@ module Bunny
53
56
  !@queue.empty?
54
57
  end
55
58
 
56
- def shutdown
59
+ def shutdown(wait_for_workers = false)
57
60
  @running = false
58
61
 
59
62
  @size.times do
@@ -61,6 +64,12 @@ module Bunny
61
64
  throw :terminate
62
65
  end
63
66
  end
67
+
68
+ return unless wait_for_workers && @shutdown_timeout
69
+
70
+ @shutdown_mutex.synchronize do
71
+ @shutdown_conditional.wait(@shutdown_mutex, @shutdown_timeout)
72
+ end
64
73
  end
65
74
 
66
75
  def join(timeout = nil)
@@ -102,6 +111,10 @@ module Bunny
102
111
  end
103
112
  end
104
113
  end
114
+
115
+ @shutdown_mutex.synchronize do
116
+ @shutdown_conditional.signal unless busy?
117
+ end
105
118
  end
106
119
  end
107
120
  end
@@ -337,14 +337,14 @@ module Bunny
337
337
  # opened (this operation is very fast and inexpensive).
338
338
  #
339
339
  # @return [Bunny::Channel] Newly opened channel
340
- def create_channel(n = nil, consumer_pool_size = 1, consumer_pool_abort_on_exception = false)
340
+ def create_channel(n = nil, consumer_pool_size = 1, consumer_pool_abort_on_exception = false, consumer_pool_shutdown_timeout = 60)
341
341
  raise ArgumentError, "channel number 0 is reserved in the protocol and cannot be used" if 0 == n
342
342
 
343
343
  @channel_mutex.synchronize do
344
344
  if n && (ch = @channels[n])
345
345
  ch
346
346
  else
347
- ch = Bunny::Channel.new(self, n, ConsumerWorkPool.new(consumer_pool_size || 1, consumer_pool_abort_on_exception))
347
+ ch = Bunny::Channel.new(self, n, ConsumerWorkPool.new(consumer_pool_size || 1, consumer_pool_abort_on_exception, consumer_pool_shutdown_timeout))
348
348
  ch.open
349
349
  ch
350
350
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Bunny
4
4
  # @return [String] Version of the library
5
- VERSION = "2.5.1"
5
+ VERSION = "2.6.0"
6
6
  end
@@ -73,4 +73,66 @@ describe Bunny::Consumer, "#cancel" do
73
73
  expect(delivered_data).to be_empty
74
74
  end
75
75
  end
76
+
77
+ context "with a worker pool shutdown timeout configured" do
78
+ let(:queue_name) { "bunny.queues.#{rand}" }
79
+
80
+ it "processes the message if processing completes within the timeout" do
81
+ delivered_data = []
82
+ consumer = nil
83
+
84
+ t = Thread.new do
85
+ ch = connection.create_channel(nil, 1, false, 5)
86
+ q = ch.queue(queue_name, :auto_delete => true, :durable => false)
87
+
88
+ consumer = Bunny::Consumer.new(ch, q)
89
+ consumer.on_delivery do |_, _, payload|
90
+ sleep 2
91
+ delivered_data << payload
92
+ end
93
+
94
+ q.subscribe_with(consumer, :block => false)
95
+ end
96
+ t.abort_on_exception = true
97
+ sleep 1.0
98
+
99
+ ch = connection.create_channel
100
+ ch.default_exchange.publish("", :routing_key => queue_name)
101
+ sleep 0.7
102
+
103
+ consumer.cancel
104
+ sleep 1.0
105
+
106
+ expect(delivered_data).to_not be_empty
107
+ end
108
+
109
+ it "kills the consumer if processing takes longer than the timeout" do
110
+ delivered_data = []
111
+ consumer = nil
112
+
113
+ t = Thread.new do
114
+ ch = connection.create_channel(nil, 1, false, 1)
115
+ q = ch.queue(queue_name, :auto_delete => true, :durable => false)
116
+
117
+ consumer = Bunny::Consumer.new(ch, q)
118
+ consumer.on_delivery do |_, _, payload|
119
+ sleep 3
120
+ delivered_data << payload
121
+ end
122
+
123
+ q.subscribe_with(consumer, :block => false)
124
+ end
125
+ t.abort_on_exception = true
126
+ sleep 1.0
127
+
128
+ ch = connection.create_channel
129
+ ch.default_exchange.publish("", :routing_key => queue_name)
130
+ sleep 0.7
131
+
132
+ consumer.cancel
133
+ sleep 1.0
134
+
135
+ expect(delivered_data).to be_empty
136
+ end
137
+ end
76
138
  end
@@ -1,23 +1,30 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  require "spec_helper"
3
3
 
4
- unless ENV["CI"]
5
- describe "x-consistent-hash exchange" do
6
- let(:connection) do
7
- c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed")
8
- c.start
9
- c
10
- end
4
+ describe "x-consistent-hash exchange" do
5
+ let(:connection) do
6
+ c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed",
7
+ :automatically_recover => true)
8
+ c.start
9
+ c
10
+ end
11
+
12
+ let(:http_client) { RabbitMQ::HTTP::Client.new("http://127.0.0.1:15672") }
13
+
14
+ after :each do
15
+ connection.close
16
+ end
11
17
 
12
- after :each do
13
- connection.close
14
- end
18
+ let(:list) { Range.new(0, 6).to_a.map(&:to_s) }
15
19
 
16
- let(:list) { Range.new(0, 6).to_a.map(&:to_s) }
20
+ let(:m) { 1500 }
17
21
 
18
- let(:m) { 1500 }
22
+ let(:exchange_plugin_available) do
23
+ http_client.overview.exchange_types.map(&:name).include?("x-consistent-hash")
24
+ end
19
25
 
20
- it "distributes messages between queues bound with the same routing key" do
26
+ it "distributes messages between queues bound with the same routing key" do
27
+ if exchange_plugin_available
21
28
  ch = connection.create_channel
22
29
  body = "сообщение"
23
30
  # requires the consistent hash exchange plugin,
@@ -45,6 +52,8 @@ unless ENV["CI"]
45
52
  expect(q2.message_count).to be > 100
46
53
 
47
54
  ch.close
48
- end
49
- end
55
+ else
56
+ skip "x-consistent-hash exchange type isn't available"
57
+ end # if
58
+ end # it
50
59
  end
@@ -124,6 +124,59 @@ describe Bunny::Channel do
124
124
  end
125
125
 
126
126
  include_examples "publish confirms"
127
+
128
+ it "returns only when all confirmations for publishes are received" do
129
+ ch = connection.create_channel
130
+
131
+ operations_log = []
132
+ operations_log_mutex = Mutex.new
133
+ acks_received = Queue.new
134
+
135
+ log_acks = proc do |tag, _, is_nack|
136
+ operations_log_mutex.synchronize do
137
+ operation = "#{'n' if is_nack}ack_#{tag}"
138
+ operations_log << operation unless operations_log.include?(operation)
139
+ end
140
+ acks_received << true
141
+ end
142
+
143
+ ch.confirm_select(log_acks)
144
+
145
+ x = ch.default_exchange
146
+ q = ch.temporary_queue
147
+
148
+ x.publish('msg', routing_key: q.name)
149
+
150
+ # wait for the confirmation to arrive
151
+ acks_received.pop
152
+
153
+ # artificially simulate a slower ack. the test should work properly even
154
+ # without this patch, but it's here just to be sure we catch it.
155
+ def (x.channel).handle_ack_or_nack(delivery_tag_before_offset, multiple, nack)
156
+ sleep 0.1
157
+ super
158
+ end
159
+
160
+ x.publish('msg', routing_key: q.name)
161
+ x.publish('msg', routing_key: q.name)
162
+
163
+ if x.wait_for_confirms
164
+ operations_log_mutex.synchronize do
165
+ operations_log << 'all_confirmed'
166
+ end
167
+ end
168
+
169
+ # wait for all the confirmations to arrive
170
+ acks_received.pop
171
+ acks_received.pop
172
+
173
+ expect(operations_log).to eq([
174
+ 'ack_1',
175
+ 'ack_2',
176
+ 'ack_3',
177
+ 'all_confirmed',
178
+ ])
179
+ end
127
180
  end
128
181
 
129
182
  context "with a single-threaded connection" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bunny
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.5.1
4
+ version: 2.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Duncan
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2016-08-15 00:00:00.000000000 Z
15
+ date: 2016-10-14 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: amq-protocol