bunny 1.2.0 → 1.2.1

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: f18caee12de5783891349254c21dc99fac3bf763
4
- data.tar.gz: df8fbda1c395fe0627b0c563d80410aa1064af54
3
+ metadata.gz: c8f1c8052f218b6d4ed95579a93e03ee9014fea5
4
+ data.tar.gz: 739d9b25a80d0c6063124229fe8226fe45390ed3
5
5
  SHA512:
6
- metadata.gz: 2b264d5692cb7840a520c0b101e042fd9c09b98ac2de888afedb2aecbd12d2b5cde655459d25982bc63e987f2b2fc69160744e96f3ccc45f0a35d655883e4559
7
- data.tar.gz: de3bc0b5d8ee2118cc8b8ba1bc459f2b4e5f96a3af619d420f2e52a7f86df46ca16dabb4aff99509de8cc4387c1c1bc04dd509637af89353e9b9639764a87b66
6
+ metadata.gz: 0fe50e8b79c8611edf5f6b3f152b21680fdcb9790bf73126a2c3e4e2bbc38d485b26cef1edf0bde99eca2e8d34c784776396cd4cf8a26829b53ca7974de45430
7
+ data.tar.gz: 2cb6bf110f3c9508eb055e37c2aebac6ff0915e6d89e014934738b9671041e6b34a9af13aca165d057d296a73efd6f2497efaaa4c0049726b9a362cf31c26e35
@@ -1,3 +1,20 @@
1
+ ## Changes between Bunny 1.2.0 and 1.2.1
2
+
3
+ ### Better Synchronization for Publisher Confirms
4
+
5
+ Publisher confirms implementation now synchronizes unconfirmed
6
+ set better.
7
+
8
+ Contributed by Nicolas Viennot.
9
+
10
+ ### Channel Allocation After Recovery
11
+
12
+ Channel id allocator is no longer reset after recovery
13
+ if there are channels open. Makes it possible to open channels
14
+ on a recovered connection (in addition to the channels
15
+ it already had).
16
+
17
+
1
18
  ## Changes between Bunny 1.1.0 and 1.2.0
2
19
 
3
20
  ### :key Supported in Bunny::Channel#queue_bind
@@ -157,6 +157,9 @@ module Bunny
157
157
  # @return [Hash<String, Bunny::Consumer>] Consumer instances declared on this channel
158
158
  attr_reader :consumers
159
159
 
160
+ # @return [Integer] active basic.qos prefetch value
161
+ attr_reader :prefetch_count
162
+
160
163
  DEFAULT_CONTENT_TYPE = "application/octet-stream".freeze
161
164
  SHORTSTR_LIMIT = 255
162
165
 
@@ -412,8 +415,8 @@ module Bunny
412
415
  # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
413
416
  # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
414
417
  # @api public
415
- def prefetch(prefetch_count)
416
- self.basic_qos(prefetch_count, false)
418
+ def prefetch(count)
419
+ self.basic_qos(count, false)
417
420
  end
418
421
 
419
422
  # Flow control. When set to false, RabbitMQ will stop delivering messages on this
@@ -536,8 +539,10 @@ module Bunny
536
539
  opts[:priority] ||= 0
537
540
 
538
541
  if @next_publish_seq_no > 0
539
- @unconfirmed_set.add(@next_publish_seq_no)
540
- @next_publish_seq_no += 1
542
+ @unconfirmed_set_mutex.synchronize do
543
+ @unconfirmed_set.add(@next_publish_seq_no)
544
+ @next_publish_seq_no += 1
545
+ end
541
546
  end
542
547
 
543
548
  frames = AMQ::Protocol::Basic::Publish.encode(@id,
@@ -604,18 +609,18 @@ module Bunny
604
609
  # @see Bunny::Channel#prefetch
605
610
  # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
606
611
  # @api public
607
- def basic_qos(prefetch_count, global = false)
608
- raise ArgumentError.new("prefetch count must be a positive integer, given: #{prefetch_count}") if prefetch_count < 0
612
+ def basic_qos(count, global = false)
613
+ raise ArgumentError.new("prefetch count must be a positive integer, given: #{prefetch_count}") if count < 0
609
614
  raise_if_no_longer_open!
610
615
 
611
- @connection.send_frame(AMQ::Protocol::Basic::Qos.encode(@id, 0, prefetch_count, global))
616
+ @connection.send_frame(AMQ::Protocol::Basic::Qos.encode(@id, 0, count, global))
612
617
 
613
618
  Bunny::Timeout.timeout(read_write_timeout, ClientTimeout) do
614
619
  @last_basic_qos_ok = wait_on_continuations
615
620
  end
616
621
  raise_if_continuation_resulted_in_a_channel_error!
617
622
 
618
- @prefetch_count = prefetch_count
623
+ @prefetch_count = count
619
624
 
620
625
  @last_basic_qos_ok
621
626
  end
@@ -1321,6 +1326,11 @@ module Bunny
1321
1326
  @last_tx_rollback_ok
1322
1327
  end
1323
1328
 
1329
+ # @return [Boolean] true if this channel has transactions enabled
1330
+ def using_tx?
1331
+ !!@tx_mode
1332
+ end
1333
+
1324
1334
  # @endgroup
1325
1335
 
1326
1336
 
@@ -1332,6 +1342,7 @@ module Bunny
1332
1342
  def using_publisher_confirmations?
1333
1343
  @next_publish_seq_no > 0
1334
1344
  end
1345
+ alias using_publisher_confirms? using_publisher_confirmations?
1335
1346
 
1336
1347
  # Enables publisher confirms for the channel.
1337
1348
  # @return [AMQ::Protocol::Confirm::SelectOk] RabbitMQ response
@@ -1681,23 +1692,23 @@ module Bunny
1681
1692
 
1682
1693
  # @private
1683
1694
  def handle_ack_or_nack(delivery_tag, multiple, nack)
1684
- if nack
1685
- cloned_set = @unconfirmed_set.clone
1695
+ @unconfirmed_set_mutex.synchronize do
1696
+ if nack
1697
+ cloned_set = @unconfirmed_set.clone
1698
+ if multiple
1699
+ cloned_set.keep_if { |i| i <= delivery_tag }
1700
+ @nacked_set.merge(cloned_set)
1701
+ else
1702
+ @nacked_set.add(delivery_tag)
1703
+ end
1704
+ end
1705
+
1686
1706
  if multiple
1687
- cloned_set.keep_if { |i| i <= delivery_tag }
1688
- @nacked_set.merge(cloned_set)
1707
+ @unconfirmed_set.delete_if { |i| i <= delivery_tag }
1689
1708
  else
1690
- @nacked_set.add(delivery_tag)
1709
+ @unconfirmed_set.delete(delivery_tag)
1691
1710
  end
1692
- end
1693
-
1694
- if multiple
1695
- @unconfirmed_set.delete_if { |i| i <= delivery_tag }
1696
- else
1697
- @unconfirmed_set.delete(delivery_tag)
1698
- end
1699
1711
 
1700
- @unconfirmed_set_mutex.synchronize do
1701
1712
  @only_acks_received = (@only_acks_received && !nack)
1702
1713
 
1703
1714
  @confirms_continuations.push(true) if @unconfirmed_set.empty?
@@ -975,7 +975,11 @@ module Bunny
975
975
  @logger.debug "Heartbeat interval negotiation: client = #{@client_heartbeat}, server = #{connection_tune.heartbeat}, result = #{@heartbeat}"
976
976
  @logger.info "Heartbeat interval used (in seconds): #{@heartbeat}"
977
977
 
978
- @channel_id_allocator = ChannelIdAllocator.new(@channel_max)
978
+ # if there are existing channels we've just recovered from
979
+ # a network failure and need to fix the allocated set. See issue 205. MK.
980
+ if @channels.empty?
981
+ @channel_id_allocator = ChannelIdAllocator.new(@channel_max)
982
+ end
979
983
 
980
984
  @transport.send_frame(AMQ::Protocol::Connection::TuneOk.encode(@channel_max, @frame_max, @heartbeat))
981
985
  @logger.debug "Sent connection.tune-ok with heartbeat interval = #{@heartbeat}, frame_max = #{@frame_max}, channel_max = #{@channel_max}"
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Bunny
4
4
  # @return [String] Version of the library
5
- VERSION = "1.2.0"
5
+ VERSION = "1.2.1"
6
6
  end
@@ -0,0 +1,234 @@
1
+ require "spec_helper"
2
+ require "rabbitmq/http/client"
3
+
4
+ unless ENV["CI"]
5
+ describe "Connection recovery" do
6
+ let(:connection) { }
7
+ let(:http_client) { RabbitMQ::HTTP::Client.new("http://127.0.0.1:15672") }
8
+
9
+ def close_all_connections!
10
+ http_client.list_connections.each do |conn_info|
11
+ http_client.close_connection(conn_info.name)
12
+ end
13
+ end
14
+
15
+ def wait_for_recovery
16
+ sleep 0.7
17
+ end
18
+
19
+ def with_open(c = Bunny.new(:network_recovery_interval => 0.2, :recover_from_connection_close => true), &block)
20
+ begin
21
+ c.start
22
+ block.call(c)
23
+ ensure
24
+ c.close
25
+ end
26
+ end
27
+
28
+ def ensure_queue_recovery(ch, q)
29
+ q.purge
30
+ x = ch.default_exchange
31
+ x.publish("msg", :routing_key => q.name)
32
+ sleep 0.2
33
+ q.message_count.should == 1
34
+ q.purge
35
+ end
36
+
37
+ def ensure_queue_binding_recovery(x, q, routing_key = "")
38
+ q.purge
39
+ x.publish("msg", :routing_key => routing_key)
40
+ sleep 0.2
41
+ q.message_count.should == 1
42
+ q.purge
43
+ end
44
+
45
+ def ensure_exchange_binding_recovery(ch, source, destination, routing_key = "")
46
+ q = ch.queue("", :exclusive => true)
47
+ q.bind(destination, :routing_key => routing_key)
48
+
49
+ source.publish("msg", :routing_key => routing_key)
50
+ q.message_count.should == 1
51
+ q.delete
52
+ end
53
+
54
+ #
55
+ # Examples
56
+ #
57
+
58
+ it "reconnects after grace period" do
59
+ with_open do |c|
60
+ close_all_connections!
61
+ sleep 0.1
62
+ c.should_not be_open
63
+
64
+ wait_for_recovery
65
+ c.should be_open
66
+ end
67
+ end
68
+
69
+ it "recovers channel" do
70
+ with_open do |c|
71
+ ch1 = c.create_channel
72
+ ch2 = c.create_channel
73
+ close_all_connections!
74
+ sleep 0.1
75
+ c.should_not be_open
76
+
77
+ wait_for_recovery
78
+ ch1.should be_open
79
+ ch2.should be_open
80
+ end
81
+ end
82
+
83
+ it "recovers basic.qos prefetch setting" do
84
+ with_open do |c|
85
+ ch = c.create_channel
86
+ ch.prefetch(11)
87
+ ch.prefetch_count.should == 11
88
+ close_all_connections!
89
+ sleep 0.1
90
+ c.should_not be_open
91
+
92
+ wait_for_recovery
93
+ ch.should be_open
94
+ ch.prefetch_count.should == 11
95
+ end
96
+ end
97
+
98
+
99
+ it "recovers publisher confirms setting" do
100
+ with_open do |c|
101
+ ch = c.create_channel
102
+ ch.confirm_select
103
+ ch.should be_using_publisher_confirms
104
+ close_all_connections!
105
+ sleep 0.1
106
+ c.should_not be_open
107
+
108
+ wait_for_recovery
109
+ ch.should be_open
110
+ ch.should be_using_publisher_confirms
111
+ end
112
+ end
113
+
114
+ it "recovers transactionality setting" do
115
+ with_open do |c|
116
+ ch = c.create_channel
117
+ ch.tx_select
118
+ ch.should be_using_tx
119
+ close_all_connections!
120
+ sleep 0.1
121
+ c.should_not be_open
122
+
123
+ wait_for_recovery
124
+ ch.should be_open
125
+ ch.should be_using_tx
126
+ end
127
+ end
128
+
129
+ it "recovers client-named queues" do
130
+ with_open do |c|
131
+ ch = c.create_channel
132
+ q = ch.queue("bunny.tests.recovery.client-named#{rand}")
133
+ close_all_connections!
134
+ sleep 0.1
135
+ c.should_not be_open
136
+
137
+ wait_for_recovery
138
+ ch.should be_open
139
+ ensure_queue_recovery(ch, q)
140
+ q.delete
141
+ end
142
+ end
143
+
144
+
145
+ it "recovers server-named queues" do
146
+ with_open do |c|
147
+ ch = c.create_channel
148
+ q = ch.queue("", :exclusive => true)
149
+ close_all_connections!
150
+ sleep 0.1
151
+ c.should_not be_open
152
+
153
+ wait_for_recovery
154
+ ch.should be_open
155
+ ensure_queue_recovery(ch, q)
156
+ end
157
+ end
158
+
159
+ it "recovers queue bindings" do
160
+ with_open do |c|
161
+ ch = c.create_channel
162
+ x = ch.fanout("amq.fanout")
163
+ q = ch.queue("", :exclusive => true)
164
+ q.bind(x)
165
+ close_all_connections!
166
+ sleep 0.1
167
+ c.should_not be_open
168
+
169
+ wait_for_recovery
170
+ ch.should be_open
171
+ ensure_queue_binding_recovery(x, q)
172
+ end
173
+ end
174
+
175
+ it "recovers exchange bindings" do
176
+ with_open do |c|
177
+ ch = c.create_channel
178
+ x = ch.fanout("amq.fanout")
179
+ x2 = ch.fanout("bunny.tests.recovery.fanout")
180
+ x2.bind(x)
181
+ close_all_connections!
182
+ sleep 0.1
183
+ c.should_not be_open
184
+
185
+ wait_for_recovery
186
+ ch.should be_open
187
+ ensure_exchange_binding_recovery(ch, x, x2)
188
+ end
189
+ end
190
+
191
+ it "recovers allocated channel ids" do
192
+ with_open do |c|
193
+ q = "queue#{Time.now.to_i}"
194
+ 10.times { c.create_channel }
195
+ c.queue_exists?(q).should be_false
196
+ close_all_connections!
197
+ sleep 0.1
198
+ c.should_not be_open
199
+
200
+ wait_for_recovery
201
+ c.queue_exists?(q).should be_false
202
+ # make sure the connection isn't closed shortly after
203
+ # due to "second 'channel.open' seen". MK.
204
+ c.should be_open
205
+ sleep 0.1
206
+ c.should be_open
207
+ sleep 0.1
208
+ c.should be_open
209
+ end
210
+ end
211
+
212
+ it "recovers consumer" do
213
+ with_open do |c|
214
+ delivered = false
215
+
216
+ ch = c.create_channel
217
+ q = ch.queue("", :exclusive => true)
218
+ q.subscribe do |_, _, _|
219
+ delivered = true
220
+ end
221
+ close_all_connections!
222
+ sleep 0.1
223
+ c.should_not be_open
224
+
225
+ wait_for_recovery
226
+ ch.should be_open
227
+
228
+ q.publish("")
229
+ sleep 0.5
230
+ expect(delivered).to be_true
231
+ end
232
+ end
233
+ end
234
+ end
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: 1.2.0
4
+ version: 1.2.1
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: 2014-04-09 00:00:00.000000000 Z
15
+ date: 2014-04-11 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: amq-protocol
@@ -152,6 +152,7 @@ files:
152
152
  - spec/higher_level_api/integration/channel_close_spec.rb
153
153
  - spec/higher_level_api/integration/channel_open_spec.rb
154
154
  - spec/higher_level_api/integration/confirm_select_spec.rb
155
+ - spec/higher_level_api/integration/connection_recovery_spec.rb
155
156
  - spec/higher_level_api/integration/connection_spec.rb
156
157
  - spec/higher_level_api/integration/connection_stop_spec.rb
157
158
  - spec/higher_level_api/integration/consistent_hash_exchange_spec.rb
@@ -248,6 +249,7 @@ test_files:
248
249
  - spec/higher_level_api/integration/channel_close_spec.rb
249
250
  - spec/higher_level_api/integration/channel_open_spec.rb
250
251
  - spec/higher_level_api/integration/confirm_select_spec.rb
252
+ - spec/higher_level_api/integration/connection_recovery_spec.rb
251
253
  - spec/higher_level_api/integration/connection_spec.rb
252
254
  - spec/higher_level_api/integration/connection_stop_spec.rb
253
255
  - spec/higher_level_api/integration/consistent_hash_exchange_spec.rb