bunny 2.5.1 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
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