bunny 1.2.0 → 1.2.1

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: 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