bunny 0.9.0.pre5 → 0.9.0.pre6

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -18,3 +18,4 @@ playground/*
18
18
  *.org
19
19
  repl-*
20
20
  debug/*
21
+ *.dump
data/ChangeLog.md CHANGED
@@ -1,3 +1,49 @@
1
+ ## Changes between Bunny 0.9.0.pre5 and 0.9.0.pre6
2
+
3
+ ### Automatic Network Failure Recovery
4
+
5
+ Automatic Network Failure Recovery is a new Bunny feature that was earlier
6
+ impemented and vetted out in [amqp gem](http://rubyamqp.info). What it does
7
+ is, when a network activity loop detects an issue, it will try to
8
+ periodically recover [first TCP, then] AMQP 0.9.1 connection, reopen
9
+ all channels, recover all exchanges, queues, bindings and consumers
10
+ on those channels (to be clear: this only includes entities and consumers added via
11
+ Bunny).
12
+
13
+ Publishers and consumers will continue operating shortly after the network
14
+ connection recovers.
15
+
16
+ Learn more in the [Error Handling and Recovery](http://rubybunny.info/articles/error_handling.html)
17
+ documentation guide.
18
+
19
+ ### Confirms Listeners
20
+
21
+ Bunny now supports listeners (callbacks) on
22
+
23
+ ``` ruby
24
+ ch.confirm_select do |delivery_tag, multiple, nack|
25
+ # handle confirms (e.g. perform retries) here
26
+ end
27
+ ```
28
+
29
+ Contributed by Greg Brockman.
30
+
31
+ ### Publisher Confirms Improvements
32
+
33
+ Publisher confirms implementation now uses non-strict equality (`<=`) for
34
+ cases when multiple messages are confirmed by RabbitMQ at once.
35
+
36
+ `Bunny::Channel#unconfirmed_set` is now part of the public API that lets
37
+ developers access unconfirmed delivery tags to perform retries and such.
38
+
39
+ Contributed by Greg Brockman.
40
+
41
+ ### Publisher Confirms Concurrency Fix
42
+
43
+ `Bunny::Channel#wait_for_confirms` will now correctly block the calling
44
+ thread until all pending confirms are received.
45
+
46
+
1
47
  ## Changes between Bunny 0.9.0.pre4 and 0.9.0.pre5
2
48
 
3
49
  ### Channel Errors Reset
data/README.md CHANGED
@@ -37,7 +37,7 @@ gem install bunny --pre
37
37
  To use Bunny 0.9.x in a project managed with Bundler:
38
38
 
39
39
  ``` ruby
40
- gem "bunny", ">= 0.9.0.pre4" # optionally: , :git => "git://github.com/ruby-amqp/bunny.git", :branch => "master"
40
+ gem "bunny", ">= 0.9.0.pre6" # optionally: , :git => "git://github.com/ruby-amqp/bunny.git", :branch => "master"
41
41
  ```
42
42
 
43
43
 
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require "bundler"
5
+ Bundler.setup
6
+
7
+ $:.unshift(File.expand_path("../../../lib", __FILE__))
8
+
9
+ require 'bunny'
10
+
11
+ begin
12
+ conn = Bunny.new("amqp://guest8we78w7e8:guest2378278@127.0.0.1")
13
+ conn.start
14
+ rescue Bunny::PossibleAuthenticationFailureError => e
15
+ puts "Could not authenticate as #{conn.username}"
16
+ end
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require "bundler"
5
+ Bundler.setup
6
+
7
+ $:.unshift(File.expand_path("../../../lib", __FILE__))
8
+
9
+ require 'bunny'
10
+
11
+ conn = Bunny.new(:heartbeat_interval => 8)
12
+ conn.start
13
+
14
+ ch = conn.create_channel
15
+ x = ch.topic("bunny.examples.recovery.topic", :durable => false)
16
+ q = ch.queue("bunny.examples.recovery.client_named_queue1", :durable => false)
17
+
18
+ q.bind(x, :routing_key => "abc").bind(x, :routing_key => "def")
19
+
20
+ q.subscribe do |delivery_info, metadata, payload|
21
+ puts "Consumed #{payload}"
22
+ end
23
+
24
+ loop do
25
+ sleep 3
26
+ puts "Tick"
27
+ x.publish(rand.to_s, :routing_key => ["abc", "def", "ghi", "xyz"].sample)
28
+ end
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require "bundler"
5
+ Bundler.setup
6
+
7
+ $:.unshift(File.expand_path("../../../lib", __FILE__))
8
+
9
+ require 'bunny'
10
+
11
+ conn = Bunny.new(:heartbeat_interval => 8)
12
+ conn.start
13
+
14
+ ch1 = conn.create_channel
15
+ x1 = ch1.topic("bunny.examples.recovery.e1", :durable => false)
16
+ q1 = ch1.queue("bunny.examples.recovery.q1", :durable => false)
17
+
18
+ q1.bind(x1, :routing_key => "abc").bind(x1, :routing_key => "def")
19
+
20
+ ch2 = conn.create_channel
21
+ x2 = ch2.topic("bunny.examples.recovery.e2", :durable => false)
22
+ q2 = ch2.queue("bunny.examples.recovery.q2", :durable => false)
23
+
24
+ q2.bind(x2, :routing_key => "abc").bind(x2, :routing_key => "def")
25
+
26
+ q1.subscribe do |delivery_info, metadata, payload|
27
+ puts "Consumed #{payload} at stage one"
28
+ x2.publish(payload, :routing_key => ["abc", "def", "xyz"].sample)
29
+ end
30
+
31
+ q2.subscribe do |delivery_info, metadata, payload|
32
+ puts "Consumed #{payload} at stage two"
33
+ end
34
+
35
+ loop do
36
+ sleep 3
37
+ rk = ["abc", "def", "ghi", "xyz"].sample
38
+ puts "Publishing with routing key #{rk}"
39
+ x1.publish(rand.to_s, :routing_key => rk)
40
+ end
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require "bundler"
5
+ Bundler.setup
6
+
7
+ $:.unshift(File.expand_path("../../../lib", __FILE__))
8
+
9
+ require 'bunny'
10
+
11
+ conn = Bunny.new(:heartbeat_interval => 8)
12
+ conn.start
13
+
14
+ ch = conn.create_channel
15
+ x = ch.topic("bunny.examples.recovery.topic", :durable => false)
16
+ q = ch.queue("", :durable => false)
17
+
18
+ q.bind(x, :routing_key => "abc").bind(x, :routing_key => "def")
19
+
20
+ q.subscribe do |delivery_info, metadata, payload|
21
+ puts "Consumed #{payload}"
22
+ end
23
+
24
+ loop do
25
+ sleep 3
26
+ puts "Tick"
27
+ x.publish(rand.to_s, :routing_key => ["abc", "def", "ghi", "xyz"].sample)
28
+ end
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require "bundler"
5
+ Bundler.setup
6
+
7
+ $:.unshift(File.expand_path("../../../lib", __FILE__))
8
+
9
+ require 'bunny'
10
+
11
+ conn = Bunny.new(:heartbeat_interval => 8)
12
+ conn.start
13
+
14
+ begin
15
+ ch1 = conn.create_channel
16
+ ch1.queue_delete("queue_that_should_not_exist#{rand}")
17
+ rescue Bunny::NotFound => e
18
+ puts "Channel-level exception! Code: #{e.channel_close.reply_code}, message: #{e.channel_close.reply_text}"
19
+ end
20
+
21
+
22
+ begin
23
+ ch2 = conn.create_channel
24
+ q = "bunny.examples.recovery.q#{rand}"
25
+
26
+ ch2.queue_declare(q, :durable => false)
27
+ ch2.queue_declare(q, :durable => true)
28
+ rescue Bunny::PreconditionFailed => e
29
+ puts "Channel-level exception! Code: #{e.channel_close.reply_code}, message: #{e.channel_close.reply_text}"
30
+ ensure
31
+ conn.create_channel.queue_delete(q)
32
+ end
33
+
34
+ puts "Disconnecting..."
35
+ conn.close
@@ -8,6 +8,9 @@ $:.unshift(File.expand_path("../../../lib", __FILE__))
8
8
 
9
9
  require 'bunny'
10
10
 
11
- conn = Bunny.new("amqp://guest:guest@aksjhdkajshdkj.example82737.com")
12
- conn.start
13
-
11
+ begin
12
+ conn = Bunny.new("amqp://guest:guest@aksjhdkajshdkj.example82737.com")
13
+ conn.start
14
+ rescue Bunny::TCPConnectionFailed => e
15
+ puts "Connection to #{conn.hostname} failed"
16
+ end
data/lib/bunny/channel.rb CHANGED
@@ -18,7 +18,7 @@ module Bunny
18
18
  #
19
19
 
20
20
  attr_accessor :id, :connection, :status, :work_pool
21
- attr_reader :next_publish_seq_no, :queues, :exchanges
21
+ attr_reader :next_publish_seq_no, :queues, :exchanges, :unconfirmed_set, :consumers
22
22
 
23
23
 
24
24
  def initialize(connection = nil, id = nil, work_pool = ConsumerWorkPool.new(1))
@@ -215,6 +215,8 @@ module Bunny
215
215
  end
216
216
  raise_if_continuation_resulted_in_a_channel_error!
217
217
 
218
+ @prefetch_count = prefetch_count
219
+
218
220
  @last_basic_qos_ok
219
221
  end
220
222
 
@@ -572,15 +574,17 @@ module Bunny
572
574
 
573
575
  # confirm.*
574
576
 
575
- def confirm_select
577
+ def confirm_select(callback=nil)
576
578
  raise_if_no_longer_open!
577
579
 
578
580
  if @next_publish_seq_no == 0
579
- @confirms_continuations = []
581
+ @confirms_continuations = ::Queue.new
580
582
  @unconfirmed_set = Set.new
581
583
  @next_publish_seq_no = 1
582
584
  end
583
585
 
586
+ @confirms_callback = callback
587
+
584
588
  @connection.send_frame(AMQ::Protocol::Confirm::Select.encode(@id, false))
585
589
  Bunny::Timer.timeout(1, ClientTimeout) do
586
590
  @last_confirm_select_ok = @continuations.pop
@@ -597,6 +601,47 @@ module Bunny
597
601
  end
598
602
 
599
603
 
604
+ #
605
+ # Recovery
606
+ #
607
+
608
+ def recover_from_network_failure
609
+ # puts "Recovering channel #{@id} from network failure..."
610
+ recover_prefetch_setting
611
+ recover_exchanges
612
+ # this includes recovering bindings
613
+ recover_queues
614
+ recover_consumers
615
+ end
616
+
617
+ def recover_prefetch_setting
618
+ basic_qos(@prefetch_count) if @prefetch_count
619
+ end
620
+
621
+ def recover_exchanges
622
+ @exchanges.values.dup.each do |x|
623
+ x.recover_from_network_failure
624
+ end
625
+ end
626
+
627
+ def recover_queues
628
+ @queues.values.dup.each do |q|
629
+ q.recover_from_network_failure
630
+ end
631
+ end
632
+
633
+ def recover_consumers
634
+ unless @consumers.empty?
635
+ @work_pool = ConsumerWorkPool.new(@work_pool.size)
636
+ @work_pool.start
637
+ end
638
+ @consumers.values.dup.each do |c|
639
+ c.recover_from_network_failure
640
+ end
641
+ end
642
+
643
+
644
+
600
645
  #
601
646
  # Implementation
602
647
  #
@@ -660,10 +705,8 @@ module Bunny
660
705
  when AMQ::Protocol::Confirm::SelectOk then
661
706
  @continuations.push(method)
662
707
  when AMQ::Protocol::Basic::Ack then
663
- # TODO: implement confirm listeners
664
708
  handle_ack_or_nack(method.delivery_tag, method.multiple, false)
665
709
  when AMQ::Protocol::Basic::Nack then
666
- # TODO: implement confirm listeners
667
710
  handle_ack_or_nack(method.delivery_tag, method.multiple, true)
668
711
  when AMQ::Protocol::Channel::Close then
669
712
  # puts "Exception on channel #{@id}: #{method.reply_code} #{method.reply_text}"
@@ -711,7 +754,7 @@ module Bunny
711
754
 
712
755
  def handle_ack_or_nack(delivery_tag, multiple, nack)
713
756
  if multiple
714
- @unconfirmed_set.delete_if { |i| i < delivery_tag }
757
+ @unconfirmed_set.delete_if { |i| i <= delivery_tag }
715
758
  else
716
759
  @unconfirmed_set.delete(delivery_tag)
717
760
  end
@@ -720,6 +763,8 @@ module Bunny
720
763
  @only_acks_received = (@only_acks_received && !nack)
721
764
 
722
765
  @confirms_continuations.push(true) if @unconfirmed_set.empty?
766
+
767
+ @confirms_callback.call(delivery_tag, multiple, nack) if @confirms_callback
723
768
  end
724
769
  end
725
770
 
@@ -730,6 +775,14 @@ module Bunny
730
775
  @work_pool.start unless @work_pool.started?
731
776
  end
732
777
 
778
+ def maybe_pause_consumer_work_pool!
779
+ @work_pool.pause if @work_pool && @work_pool.started?
780
+ end
781
+
782
+ def maybe_kill_consumer_work_pool!
783
+ @work_pool.kill if @work_pool && @work_pool.started?
784
+ end
785
+
733
786
  def read_next_frame(options = {})
734
787
  @connection.read_next_frame(options = {})
735
788
  end
@@ -739,11 +792,15 @@ module Bunny
739
792
  def synchronize(&block)
740
793
  @publishing_mutex.synchronize(&block)
741
794
  end
742
-
795
+
743
796
  def deregister_queue(queue)
744
797
  @queues.delete(queue.name)
745
798
  end
746
799
 
800
+ def deregister_queue_named(name)
801
+ @queues.delete(name)
802
+ end
803
+
747
804
  def register_queue(queue)
748
805
  @queues[queue.name] = queue
749
806
  end
@@ -751,7 +808,7 @@ module Bunny
751
808
  def find_queue(name)
752
809
  @queues[name]
753
810
  end
754
-
811
+
755
812
  def deregister_exchange(exchange)
756
813
  @exchanges.delete(exchange.name)
757
814
  end
@@ -58,5 +58,13 @@ module Bunny
58
58
  def inspect
59
59
  "#<#{self.class.name}:#{object_id} @channel_id=#{@channel.number} @queue=#{self.queue_name}> @consumer_tag=#{@consumer_tag} @exclusive=#{@exclusive} @no_ack=#{@no_ack}>"
60
60
  end
61
+
62
+ #
63
+ # Recovery
64
+ #
65
+
66
+ def recover_from_network_failure
67
+ @channel.basic_consume_with(self)
68
+ end
61
69
  end
62
70
  end
@@ -9,6 +9,8 @@ module Bunny
9
9
  # API
10
10
  #
11
11
 
12
+ attr_reader :size
13
+
12
14
  def initialize(size = 1)
13
15
  @size = size
14
16
  @queue = ::Queue.new
@@ -46,6 +48,18 @@ module Bunny
46
48
  @threads.each { |t| t.join }
47
49
  end
48
50
 
51
+ def pause
52
+ @threads.each { |t| t.stop }
53
+ end
54
+
55
+ def resume
56
+ @threads.each { |t| t.run }
57
+ end
58
+
59
+ def kill
60
+ @threads.each { |t| t.kill }
61
+ end
62
+
49
63
  protected
50
64
 
51
65
  def run_loop
@@ -16,9 +16,9 @@ module Bunny
16
16
  class ConnectionClosedError < StandardError
17
17
  def initialize(frame)
18
18
  if frame.respond_to?(:method_class)
19
- super("Trying to send frame through a closed connection. Frame is #{frame.inspect}")
20
- else
21
19
  super("Trying to send frame through a closed connection. Frame is #{frame.inspect}, method class is #{frame.method_class}")
20
+ else
21
+ super("Trying to send frame through a closed connection. Frame is #{frame.inspect}")
22
22
  end
23
23
  end
24
24
  end
@@ -44,7 +44,6 @@ module Bunny
44
44
  ConnectionError = TCPConnectionFailed
45
45
  ServerDownError = TCPConnectionFailed
46
46
 
47
- # TODO
48
47
  class ForcedChannelCloseError < StandardError; end
49
48
  class ForcedConnectionCloseError < StandardError; end
50
49
  class MessageError < StandardError; end
@@ -131,4 +130,10 @@ module Bunny
131
130
 
132
131
  class ChannelError < ConnectionLevelException
133
132
  end
133
+
134
+ class InvalidCommand < ConnectionLevelException
135
+ end
136
+
137
+ class UnexpectedFrame < ConnectionLevelException
138
+ end
134
139
  end
@@ -126,6 +126,11 @@ module Bunny
126
126
  self
127
127
  end
128
128
 
129
+ def recover_from_network_failure
130
+ # puts "Recovering exchange #{@name} from network failure"
131
+ declare! unless predefined?
132
+ end
133
+
129
134
 
130
135
  #
131
136
  # Implementation
@@ -139,6 +144,11 @@ module Bunny
139
144
  end
140
145
  end
141
146
 
147
+ # @return [Boolean] true if this exchange is a pre-defined one (amq.direct, amq.fanout, amq.match and so on)
148
+ def predefined?
149
+ @name && ((@name == AMQ::Protocol::EMPTY_STRING) || !!(@name =~ /^amq\./i))
150
+ end # predefined?
151
+
142
152
  protected
143
153
 
144
154
  # @private
@@ -18,10 +18,15 @@ module Bunny
18
18
  @thread = Thread.new(&method(:run_loop))
19
19
  end
20
20
 
21
+ def resume
22
+ start
23
+ end
24
+
25
+
21
26
  def run_loop
22
27
  loop do
23
28
  begin
24
- break if @stopping
29
+ break if @stopping || @network_is_down
25
30
 
26
31
  frame = @transport.read_next_frame
27
32
  @session.signal_activity!
@@ -50,6 +55,10 @@ module Bunny
50
55
  # should happen per operation and not in this loop
51
56
  rescue Errno::EBADF => ebadf
52
57
  # ignored, happens when we loop after the transport has already been closed
58
+ rescue AMQ::Protocol::EmptyResponseError, IOError, Errno::EPIPE, Errno::EAGAIN => e
59
+ puts "Exception in the main loop: #{e.class.name}"
60
+ @network_is_down = true
61
+ @session.handle_network_failure(e)
53
62
  rescue Exception => e
54
63
  puts e.class.name
55
64
  puts e.message
data/lib/bunny/queue.rb CHANGED
@@ -26,6 +26,8 @@ module Bunny
26
26
  @auto_delete = @options[:auto_delete]
27
27
  @arguments = @options[:arguments]
28
28
 
29
+ @bindings = Array.new
30
+
29
31
  @default_consumer = nil
30
32
 
31
33
  declare! unless opts[:no_declare]
@@ -65,12 +67,33 @@ module Bunny
65
67
  def bind(exchange, opts = {})
66
68
  @channel.queue_bind(@name, exchange, opts)
67
69
 
70
+ exchange_name = if exchange.respond_to?(:name)
71
+ exchange.name
72
+ else
73
+ exchange
74
+ end
75
+
76
+
77
+ # store bindings for automatic recovery. We need to be very careful to
78
+ # not cause an infinite rebinding loop here when we recover. MK.
79
+ binding = { :exchange => exchange_name, :routing_key => (opts[:routing_key] || opts[:key]), :arguments => opts[:arguments] }
80
+ @bindings.push(binding) unless @bindings.include?(binding)
81
+
68
82
  self
69
83
  end
70
84
 
71
85
  def unbind(exchange, opts = {})
72
86
  @channel.queue_unbind(@name, exchange, opts)
73
87
 
88
+ exchange_name = if exchange.respond_to?(:name)
89
+ exchange.name
90
+ else
91
+ exchange
92
+ end
93
+
94
+
95
+ @bindings.delete_if { |b| b[:exchange] == exchange_name && b[:routing_key] == (opts[:routing_key] || opts[:key]) && b[:arguments] == opts[:arguments] }
96
+
74
97
  self
75
98
  end
76
99
 
@@ -84,11 +107,12 @@ module Bunny
84
107
 
85
108
  ctag = opts.fetch(:consumer_tag, @channel.generate_consumer_tag)
86
109
  consumer = Consumer.new(@channel,
87
- @name,
110
+ self,
88
111
  ctag,
89
112
  !opts[:ack],
90
113
  opts[:exclusive],
91
114
  opts[:arguments])
115
+ puts "Added consumer #{ctag} on queue #{@name}"
92
116
  consumer.on_delivery(&block)
93
117
  consumer.on_cancellation(&opts[:on_cancellation]) if opts[:on_cancellation]
94
118
 
@@ -172,6 +196,34 @@ module Bunny
172
196
  s[:consumer_count]
173
197
  end
174
198
 
199
+ #
200
+ # Recovery
201
+ #
202
+
203
+ def recover_from_network_failure
204
+ # puts "Recovering queue #{@name} from network failure"
205
+
206
+ if self.server_named?
207
+ old_name = @name.dup
208
+ @name = AMQ::Protocol::EMPTY_STRING
209
+
210
+ @channel.deregister_queue_named(old_name)
211
+ end
212
+
213
+ declare!
214
+ begin
215
+ @channel.register_queue(self)
216
+ rescue Exception => e
217
+ puts "Caught #{e.inspect} while registering #{@name}!"
218
+ end
219
+ recover_bindings
220
+ end
221
+
222
+ def recover_bindings
223
+ @bindings.each do |b|
224
+ self.bind(b[:exchange], b)
225
+ end
226
+ end
175
227
 
176
228
 
177
229
  #
data/lib/bunny/session.rb CHANGED
@@ -111,13 +111,15 @@ module Bunny
111
111
  alias ssl? uses_ssl?
112
112
 
113
113
  def start
114
- @status = :connecting
114
+ @continuations = ::Queue.new
115
+ @status = :connecting
115
116
 
116
117
  self.initialize_transport
117
118
 
118
119
  self.init_connection
119
120
  self.open_connection
120
121
 
122
+ @event_loop = nil
121
123
  self.start_main_loop
122
124
 
123
125
  @default_channel = self.create_channel
@@ -233,9 +235,7 @@ module Bunny
233
235
  def close_connection(sync = true)
234
236
  @transport.send_frame(AMQ::Protocol::Connection::Close.encode(200, "Goodbye", 0, 0))
235
237
 
236
- if @heartbeat_sender
237
- @heartbeat_sender.stop
238
- end
238
+ maybe_shutdown_heartbeat_sender
239
239
  @status = :not_connected
240
240
 
241
241
  if sync
@@ -303,6 +303,60 @@ module Bunny
303
303
  end
304
304
  end
305
305
 
306
+ def handle_network_failure(exception)
307
+ if !recovering_from_network_failure?
308
+ @recovering_from_network_failure = true
309
+ if recoverable_network_failure?(exception)
310
+ # puts "Recovering from a network failure..."
311
+ @channels.each do |n, ch|
312
+ ch.maybe_kill_consumer_work_pool!
313
+ end
314
+ maybe_shutdown_heartbeat_sender
315
+
316
+ recover_from_network_failure
317
+ else
318
+ # TODO: investigate if we can be a bit smarter here. MK.
319
+ end
320
+ end
321
+ end
322
+
323
+ def recoverable_network_failure?(exception)
324
+ # TODO: investigate if we can be a bit smarter here. MK.
325
+ true
326
+ end
327
+
328
+ def recovering_from_network_failure?
329
+ @recovering_from_network_failure
330
+ end
331
+
332
+ def recover_from_network_failure
333
+ begin
334
+ # puts "About to start recovery..."
335
+ start
336
+
337
+ if open?
338
+ @recovering_from_network_failure = false
339
+
340
+ recover_channels
341
+ end
342
+ rescue TCPConnectionFailed, AMQ::Protocol::EmptyResponseError => e
343
+ # puts "TCP connection failed, reconnecting in 5 seconds"
344
+ sleep 5.0
345
+ retry if recoverable_network_failure?(e)
346
+ end
347
+ end
348
+
349
+ def recover_channels
350
+ # default channel is reopened right after connection
351
+ # negotiation is completed, so make sure we do not try to open
352
+ # it twice. MK.
353
+ @channels.reject { |n, ch| ch == @default_channel }.each do |n, ch|
354
+ ch.open
355
+
356
+ ch.recover_from_network_failure
357
+ end
358
+ end
359
+
306
360
  def send_raw(*args)
307
361
  @transport.write(*args)
308
362
  end
@@ -311,8 +365,14 @@ module Bunny
311
365
  case frame
312
366
  when AMQ::Protocol::Connection::Close then
313
367
  klass = case frame.reply_code
368
+ when 503 then
369
+ InvalidCommand
314
370
  when 504 then
315
371
  ChannelError
372
+ when 504 then
373
+ UnexpectedFrame
374
+ else
375
+ raise "Unknown reply code: #{frame.reply_code}, text: #{frame.reply_text}"
316
376
  end
317
377
 
318
378
  klass.new("Connection-level error: #{frame.reply_text}", self, frame)
@@ -488,13 +548,18 @@ module Bunny
488
548
  end
489
549
 
490
550
  def initialize_heartbeat_sender
551
+ # puts "Initializing heartbeat sender..."
491
552
  @heartbeat_sender = HeartbeatSender.new(@transport)
492
553
  @heartbeat_sender.start(@heartbeat)
493
554
  end
494
555
 
556
+ def maybe_shutdown_heartbeat_sender
557
+ @heartbeat_sender.stop if @heartbeat_sender
558
+ end
559
+
495
560
 
496
561
  def initialize_transport
497
- @transport = Transport.new(@host, @port, @opts)
562
+ @transport = Transport.new(self, @host, @port, @opts)
498
563
  end
499
564
 
500
565
  # Sends AMQ protocol header (also known as preamble).
@@ -14,12 +14,13 @@ module Bunny
14
14
  DEFAULT_CONNECTION_TIMEOUT = 5.0
15
15
 
16
16
 
17
- attr_reader :host, :port, :socket, :connect_timeout
17
+ attr_reader :session, :host, :port, :socket, :connect_timeout
18
18
 
19
- def initialize(host, port, opts)
20
- @host = host
21
- @port = port
22
- @opts = opts
19
+ def initialize(session, host, port, opts)
20
+ @session = session
21
+ @host = host
22
+ @port = port
23
+ @opts = opts
23
24
 
24
25
  @ssl = opts[:ssl] || false
25
26
  @ssl_cert = opts[:ssl_cert]
@@ -61,24 +62,18 @@ module Bunny
61
62
  # @raise [ClientTimeout]
62
63
  def write(*args)
63
64
  begin
64
- raise Bunny::ConnectionError.new('No connection - socket has not been created', @host, @port) if !@socket
65
+ raise Bunny::ConnectionError.new("No connection: socket is nil. ", @host, @port) if !@socket
65
66
  if @read_write_timeout
66
67
  Bunny::Timer.timeout(@read_write_timeout, Bunny::ClientTimeout) do
67
- @socket.write(*args)
68
+ @socket.write(*args) if open?
68
69
  end
69
70
  else
70
- @socket.write(*args)
71
+ @socket.write(*args) if open?
71
72
  end
72
73
  rescue Errno::EPIPE, Errno::EAGAIN, Bunny::ClientTimeout, IOError => e
73
74
  close
74
75
 
75
- m = case e
76
- when String then
77
- e
78
- when Exception then
79
- e.message
80
- end
81
- raise Bunny::ConnectionError.new(m, @host, @port)
76
+ @session.handle_network_failure(e)
82
77
  end
83
78
  end
84
79
  alias send_raw write
@@ -97,7 +92,7 @@ module Bunny
97
92
  end
98
93
 
99
94
  def flush
100
- @socket.flush
95
+ @socket.flush if @socket
101
96
  end
102
97
 
103
98
  def read_fully(*args)
@@ -135,13 +130,31 @@ module Bunny
135
130
  # @private
136
131
  def send_frame(frame)
137
132
  if closed?
138
- raise ConnectionClosedError.new(frame)
133
+ @session.handle_network_failure(ConnectionClosedError.new(frame))
139
134
  else
140
135
  send_raw(frame.encode)
141
136
  end
142
137
  end
143
138
 
144
139
 
140
+ def self.reacheable?(host, port, timeout)
141
+ begin
142
+ s = Bunny::Socket.open(host, port,
143
+ :socket_timeout => timeout)
144
+
145
+ true
146
+ rescue SocketError, Timeout::Error => e
147
+ false
148
+ ensure
149
+ s.close if s
150
+ end
151
+ end
152
+
153
+ def self.ping!(host, port, timeout)
154
+ raise ConnectionTimeout.new("#{host}:#{port} is unreachable") if !reacheable?(host, port, timeout)
155
+ end
156
+
157
+
145
158
  protected
146
159
 
147
160
  def initialize_socket
data/lib/bunny/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Bunny
4
- VERSION = "0.9.0.pre5"
4
+ VERSION = "0.9.0.pre6"
5
5
  end
@@ -0,0 +1,15 @@
1
+ require "spec_helper"
2
+
3
+ describe Bunny::Transport, ".reachable?" do
4
+ it "returns true for google.com, 80" do
5
+ Bunny::Transport.reacheable?("google.com", 80, 1).should be_true
6
+ end
7
+
8
+ it "returns true for google.com, 8088" do
9
+ Bunny::Transport.reacheable?("google.com", 8088, 1).should be_false
10
+ end
11
+
12
+ it "returns false for google1237982792837.com, 8277" do
13
+ Bunny::Transport.reacheable?("google1237982792837.com", 8277, 1).should be_false
14
+ end
15
+ end
metadata CHANGED
@@ -1,213 +1,213 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: bunny
3
- version: !ruby/object:Gem::Version
4
- version: 0.9.0.pre5
3
+ version: !ruby/object:Gem::Version
5
4
  prerelease: 6
5
+ version: 0.9.0.pre6
6
6
  platform: ruby
7
- authors:
8
- - Chris Duncan
9
- - Eric Lindvall
10
- - Jakub Stastny aka botanicus
11
- - Michael S. Klishin
12
- - Stefan Kaes
13
- autorequire:
7
+ authors:
8
+ - Chris Duncan
9
+ - Eric Lindvall
10
+ - Jakub Stastny aka botanicus
11
+ - Michael S. Klishin
12
+ - Stefan Kaes
13
+ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2013-01-09 00:00:00.000000000 Z
17
- dependencies:
18
- - !ruby/object:Gem::Dependency
19
- name: amq-protocol
20
- version_requirements: !ruby/object:Gem::Requirement
21
- requirements:
22
- - - ! '>='
23
- - !ruby/object:Gem::Version
24
- version: 1.0.1
25
- none: false
26
- requirement: !ruby/object:Gem::Requirement
27
- requirements:
28
- - - ! '>='
29
- - !ruby/object:Gem::Version
30
- version: 1.0.1
31
- none: false
32
- prerelease: false
33
- type: :runtime
16
+
17
+ date: 2013-01-14 00:00:00 Z
18
+ dependencies:
19
+ - !ruby/object:Gem::Dependency
20
+ name: amq-protocol
21
+ prerelease: false
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: 1.0.1
28
+ type: :runtime
29
+ version_requirements: *id001
34
30
  description: Easy to use synchronous Ruby client for RabbitMQ
35
- email:
36
- - !binary |-
37
- Y2VsbGRlZUBnbWFpbC5jb20=
38
- - !binary |-
39
- ZXJpY0A1c3RvcHMuY29t
40
- - !binary |-
41
- c3Rhc3RueUAxMDFpZGVhcy5jeg==
42
- - !binary |-
43
- bWljaGFlbEBub3ZlbWJlcmFpbi5jb20=
44
- - !binary |-
45
- c2thZXNAcmFpbHNleHByZXNzLmRl
31
+ email:
32
+ - celldee@gmail.com
33
+ - eric@5stops.com
34
+ - stastny@101ideas.cz
35
+ - michael@novemberain.com
36
+ - skaes@railsexpress.de
46
37
  executables: []
38
+
47
39
  extensions: []
48
- extra_rdoc_files:
49
- - README.md
50
- files:
51
- - .gitignore
52
- - .rspec
53
- - .travis.yml
54
- - .yardopts
55
- - ChangeLog.md
56
- - Gemfile
57
- - LICENSE
58
- - README.md
59
- - bin/ci/before_build.sh
60
- - bunny.gemspec
61
- - examples/connection/heartbeat.rb
62
- - examples/connection/unknown_host.rb
63
- - examples/guides/exchanges/direct_exchange_routing.rb
64
- - examples/guides/exchanges/fanout_exchange_routing.rb
65
- - examples/guides/exchanges/headers_exchange_routing.rb
66
- - examples/guides/exchanges/mandatory_messages.rb
67
- - examples/guides/extensions/alternate_exchange.rb
68
- - examples/guides/extensions/basic_nack.rb
69
- - examples/guides/extensions/consumer_cancellation_notification.rb
70
- - examples/guides/extensions/dead_letter_exchange.rb
71
- - examples/guides/extensions/exchange_to_exchange_bindings.rb
72
- - examples/guides/extensions/per_message_ttl.rb
73
- - examples/guides/extensions/per_queue_message_ttl.rb
74
- - examples/guides/extensions/publisher_confirms.rb
75
- - examples/guides/extensions/queue_lease.rb
76
- - examples/guides/extensions/sender_selected_distribution.rb
77
- - examples/guides/getting_started/blabbr.rb
78
- - examples/guides/getting_started/hello_world.rb
79
- - examples/guides/getting_started/weathr.rb
80
- - examples/guides/queues/redeliveries.rb
81
- - lib/bunny.rb
82
- - lib/bunny/authentication/credentials_encoder.rb
83
- - lib/bunny/authentication/external_mechanism_encoder.rb
84
- - lib/bunny/authentication/plain_mechanism_encoder.rb
85
- - lib/bunny/channel.rb
86
- - lib/bunny/channel_id_allocator.rb
87
- - lib/bunny/compatibility.rb
88
- - lib/bunny/concurrent/condition.rb
89
- - lib/bunny/consumer.rb
90
- - lib/bunny/consumer_tag_generator.rb
91
- - lib/bunny/consumer_work_pool.rb
92
- - lib/bunny/delivery_info.rb
93
- - lib/bunny/exceptions.rb
94
- - lib/bunny/exchange.rb
95
- - lib/bunny/framing.rb
96
- - lib/bunny/heartbeat_sender.rb
97
- - lib/bunny/main_loop.rb
98
- - lib/bunny/message_properties.rb
99
- - lib/bunny/queue.rb
100
- - lib/bunny/return_info.rb
101
- - lib/bunny/session.rb
102
- - lib/bunny/socket.rb
103
- - lib/bunny/system_timer.rb
104
- - lib/bunny/transport.rb
105
- - lib/bunny/version.rb
106
- - spec/compatibility/queue_declare_spec.rb
107
- - spec/higher_level_api/integration/basic_ack_spec.rb
108
- - spec/higher_level_api/integration/basic_cancel_spec.rb
109
- - spec/higher_level_api/integration/basic_consume_spec.rb
110
- - spec/higher_level_api/integration/basic_get_spec.rb
111
- - spec/higher_level_api/integration/basic_nack_spec.rb
112
- - spec/higher_level_api/integration/basic_publish_spec.rb
113
- - spec/higher_level_api/integration/basic_qos_spec.rb
114
- - spec/higher_level_api/integration/basic_recover_spec.rb
115
- - spec/higher_level_api/integration/basic_reject_spec.rb
116
- - spec/higher_level_api/integration/basic_return_spec.rb
117
- - spec/higher_level_api/integration/channel_close_spec.rb
118
- - spec/higher_level_api/integration/channel_flow_spec.rb
119
- - spec/higher_level_api/integration/channel_open_spec.rb
120
- - spec/higher_level_api/integration/channel_open_stress_spec.rb
121
- - spec/higher_level_api/integration/confirm_select_spec.rb
122
- - spec/higher_level_api/integration/connection_spec.rb
123
- - spec/higher_level_api/integration/consumer_cancellation_notification_spec.rb
124
- - spec/higher_level_api/integration/dead_lettering_spec.rb
125
- - spec/higher_level_api/integration/exchange_bind_spec.rb
126
- - spec/higher_level_api/integration/exchange_declare_spec.rb
127
- - spec/higher_level_api/integration/exchange_delete_spec.rb
128
- - spec/higher_level_api/integration/exchange_unbind_spec.rb
129
- - spec/higher_level_api/integration/message_properties_access_spec.rb
130
- - spec/higher_level_api/integration/publisher_confirms_spec.rb
131
- - spec/higher_level_api/integration/queue_bind_spec.rb
132
- - spec/higher_level_api/integration/queue_declare_spec.rb
133
- - spec/higher_level_api/integration/queue_delete_spec.rb
134
- - spec/higher_level_api/integration/queue_purge_spec.rb
135
- - spec/higher_level_api/integration/queue_unbind_spec.rb
136
- - spec/higher_level_api/integration/sender_selected_distribution_spec.rb
137
- - spec/higher_level_api/integration/tx_commit_spec.rb
138
- - spec/higher_level_api/integration/tx_rollback_spec.rb
139
- - spec/issues/issue78_spec.rb
140
- - spec/issues/issue83_spec.rb
141
- - spec/lower_level_api/integration/basic_cancel_spec.rb
142
- - spec/lower_level_api/integration/basic_consume_spec.rb
143
- - spec/spec_helper.rb
144
- - spec/unit/bunny_spec.rb
145
- - spec/unit/concurrent/condition_spec.rb
40
+
41
+ extra_rdoc_files:
42
+ - README.md
43
+ files:
44
+ - .gitignore
45
+ - .rspec
46
+ - .travis.yml
47
+ - .yardopts
48
+ - ChangeLog.md
49
+ - Gemfile
50
+ - LICENSE
51
+ - README.md
52
+ - bin/ci/before_build.sh
53
+ - bunny.gemspec
54
+ - examples/connection/authentication_failure.rb
55
+ - examples/connection/automatic_recovery_with_client_named_queues.rb
56
+ - examples/connection/automatic_recovery_with_multiple_consumers.rb
57
+ - examples/connection/automatic_recovery_with_server_named_queues.rb
58
+ - examples/connection/channel_level_exception.rb
59
+ - examples/connection/heartbeat.rb
60
+ - examples/connection/unknown_host.rb
61
+ - examples/guides/exchanges/direct_exchange_routing.rb
62
+ - examples/guides/exchanges/fanout_exchange_routing.rb
63
+ - examples/guides/exchanges/headers_exchange_routing.rb
64
+ - examples/guides/exchanges/mandatory_messages.rb
65
+ - examples/guides/extensions/alternate_exchange.rb
66
+ - examples/guides/extensions/basic_nack.rb
67
+ - examples/guides/extensions/consumer_cancellation_notification.rb
68
+ - examples/guides/extensions/dead_letter_exchange.rb
69
+ - examples/guides/extensions/exchange_to_exchange_bindings.rb
70
+ - examples/guides/extensions/per_message_ttl.rb
71
+ - examples/guides/extensions/per_queue_message_ttl.rb
72
+ - examples/guides/extensions/publisher_confirms.rb
73
+ - examples/guides/extensions/queue_lease.rb
74
+ - examples/guides/extensions/sender_selected_distribution.rb
75
+ - examples/guides/getting_started/blabbr.rb
76
+ - examples/guides/getting_started/hello_world.rb
77
+ - examples/guides/getting_started/weathr.rb
78
+ - examples/guides/queues/redeliveries.rb
79
+ - lib/bunny.rb
80
+ - lib/bunny/authentication/credentials_encoder.rb
81
+ - lib/bunny/authentication/external_mechanism_encoder.rb
82
+ - lib/bunny/authentication/plain_mechanism_encoder.rb
83
+ - lib/bunny/channel.rb
84
+ - lib/bunny/channel_id_allocator.rb
85
+ - lib/bunny/compatibility.rb
86
+ - lib/bunny/concurrent/condition.rb
87
+ - lib/bunny/consumer.rb
88
+ - lib/bunny/consumer_tag_generator.rb
89
+ - lib/bunny/consumer_work_pool.rb
90
+ - lib/bunny/delivery_info.rb
91
+ - lib/bunny/exceptions.rb
92
+ - lib/bunny/exchange.rb
93
+ - lib/bunny/framing.rb
94
+ - lib/bunny/heartbeat_sender.rb
95
+ - lib/bunny/main_loop.rb
96
+ - lib/bunny/message_properties.rb
97
+ - lib/bunny/queue.rb
98
+ - lib/bunny/return_info.rb
99
+ - lib/bunny/session.rb
100
+ - lib/bunny/socket.rb
101
+ - lib/bunny/system_timer.rb
102
+ - lib/bunny/transport.rb
103
+ - lib/bunny/version.rb
104
+ - spec/compatibility/queue_declare_spec.rb
105
+ - spec/higher_level_api/integration/basic_ack_spec.rb
106
+ - spec/higher_level_api/integration/basic_cancel_spec.rb
107
+ - spec/higher_level_api/integration/basic_consume_spec.rb
108
+ - spec/higher_level_api/integration/basic_get_spec.rb
109
+ - spec/higher_level_api/integration/basic_nack_spec.rb
110
+ - spec/higher_level_api/integration/basic_publish_spec.rb
111
+ - spec/higher_level_api/integration/basic_qos_spec.rb
112
+ - spec/higher_level_api/integration/basic_recover_spec.rb
113
+ - spec/higher_level_api/integration/basic_reject_spec.rb
114
+ - spec/higher_level_api/integration/basic_return_spec.rb
115
+ - spec/higher_level_api/integration/channel_close_spec.rb
116
+ - spec/higher_level_api/integration/channel_flow_spec.rb
117
+ - spec/higher_level_api/integration/channel_open_spec.rb
118
+ - spec/higher_level_api/integration/channel_open_stress_spec.rb
119
+ - spec/higher_level_api/integration/confirm_select_spec.rb
120
+ - spec/higher_level_api/integration/connection_spec.rb
121
+ - spec/higher_level_api/integration/consumer_cancellation_notification_spec.rb
122
+ - spec/higher_level_api/integration/dead_lettering_spec.rb
123
+ - spec/higher_level_api/integration/exchange_bind_spec.rb
124
+ - spec/higher_level_api/integration/exchange_declare_spec.rb
125
+ - spec/higher_level_api/integration/exchange_delete_spec.rb
126
+ - spec/higher_level_api/integration/exchange_unbind_spec.rb
127
+ - spec/higher_level_api/integration/message_properties_access_spec.rb
128
+ - spec/higher_level_api/integration/publisher_confirms_spec.rb
129
+ - spec/higher_level_api/integration/queue_bind_spec.rb
130
+ - spec/higher_level_api/integration/queue_declare_spec.rb
131
+ - spec/higher_level_api/integration/queue_delete_spec.rb
132
+ - spec/higher_level_api/integration/queue_purge_spec.rb
133
+ - spec/higher_level_api/integration/queue_unbind_spec.rb
134
+ - spec/higher_level_api/integration/sender_selected_distribution_spec.rb
135
+ - spec/higher_level_api/integration/tx_commit_spec.rb
136
+ - spec/higher_level_api/integration/tx_rollback_spec.rb
137
+ - spec/issues/issue78_spec.rb
138
+ - spec/issues/issue83_spec.rb
139
+ - spec/lower_level_api/integration/basic_cancel_spec.rb
140
+ - spec/lower_level_api/integration/basic_consume_spec.rb
141
+ - spec/spec_helper.rb
142
+ - spec/unit/bunny_spec.rb
143
+ - spec/unit/concurrent/condition_spec.rb
144
+ - spec/unit/transport_spec.rb
146
145
  homepage: http://github.com/ruby-amqp/bunny
147
- licenses:
148
- - MIT
149
- post_install_message:
146
+ licenses:
147
+ - MIT
148
+ post_install_message:
150
149
  rdoc_options: []
151
- require_paths:
152
- - lib
153
- required_ruby_version: !ruby/object:Gem::Requirement
154
- requirements:
155
- - - ! '>='
156
- - !ruby/object:Gem::Version
157
- version: !binary |-
158
- MA==
150
+
151
+ require_paths:
152
+ - lib
153
+ required_ruby_version: !ruby/object:Gem::Requirement
159
154
  none: false
160
- required_rubygems_version: !ruby/object:Gem::Requirement
161
- requirements:
162
- - - !binary |-
163
- Pg==
164
- - !ruby/object:Gem::Version
165
- version: 1.3.1
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: "0"
159
+ required_rubygems_version: !ruby/object:Gem::Requirement
166
160
  none: false
161
+ requirements:
162
+ - - ">"
163
+ - !ruby/object:Gem::Version
164
+ version: 1.3.1
167
165
  requirements: []
168
- rubyforge_project:
166
+
167
+ rubyforge_project:
169
168
  rubygems_version: 1.8.24
170
- signing_key:
169
+ signing_key:
171
170
  specification_version: 3
172
171
  summary: Easy to use synchronous Ruby client for RabbitMQ
173
- test_files:
174
- - spec/compatibility/queue_declare_spec.rb
175
- - spec/higher_level_api/integration/basic_ack_spec.rb
176
- - spec/higher_level_api/integration/basic_cancel_spec.rb
177
- - spec/higher_level_api/integration/basic_consume_spec.rb
178
- - spec/higher_level_api/integration/basic_get_spec.rb
179
- - spec/higher_level_api/integration/basic_nack_spec.rb
180
- - spec/higher_level_api/integration/basic_publish_spec.rb
181
- - spec/higher_level_api/integration/basic_qos_spec.rb
182
- - spec/higher_level_api/integration/basic_recover_spec.rb
183
- - spec/higher_level_api/integration/basic_reject_spec.rb
184
- - spec/higher_level_api/integration/basic_return_spec.rb
185
- - spec/higher_level_api/integration/channel_close_spec.rb
186
- - spec/higher_level_api/integration/channel_flow_spec.rb
187
- - spec/higher_level_api/integration/channel_open_spec.rb
188
- - spec/higher_level_api/integration/channel_open_stress_spec.rb
189
- - spec/higher_level_api/integration/confirm_select_spec.rb
190
- - spec/higher_level_api/integration/connection_spec.rb
191
- - spec/higher_level_api/integration/consumer_cancellation_notification_spec.rb
192
- - spec/higher_level_api/integration/dead_lettering_spec.rb
193
- - spec/higher_level_api/integration/exchange_bind_spec.rb
194
- - spec/higher_level_api/integration/exchange_declare_spec.rb
195
- - spec/higher_level_api/integration/exchange_delete_spec.rb
196
- - spec/higher_level_api/integration/exchange_unbind_spec.rb
197
- - spec/higher_level_api/integration/message_properties_access_spec.rb
198
- - spec/higher_level_api/integration/publisher_confirms_spec.rb
199
- - spec/higher_level_api/integration/queue_bind_spec.rb
200
- - spec/higher_level_api/integration/queue_declare_spec.rb
201
- - spec/higher_level_api/integration/queue_delete_spec.rb
202
- - spec/higher_level_api/integration/queue_purge_spec.rb
203
- - spec/higher_level_api/integration/queue_unbind_spec.rb
204
- - spec/higher_level_api/integration/sender_selected_distribution_spec.rb
205
- - spec/higher_level_api/integration/tx_commit_spec.rb
206
- - spec/higher_level_api/integration/tx_rollback_spec.rb
207
- - spec/issues/issue78_spec.rb
208
- - spec/issues/issue83_spec.rb
209
- - spec/lower_level_api/integration/basic_cancel_spec.rb
210
- - spec/lower_level_api/integration/basic_consume_spec.rb
211
- - spec/spec_helper.rb
212
- - spec/unit/bunny_spec.rb
213
- - spec/unit/concurrent/condition_spec.rb
172
+ test_files:
173
+ - spec/compatibility/queue_declare_spec.rb
174
+ - spec/higher_level_api/integration/basic_ack_spec.rb
175
+ - spec/higher_level_api/integration/basic_cancel_spec.rb
176
+ - spec/higher_level_api/integration/basic_consume_spec.rb
177
+ - spec/higher_level_api/integration/basic_get_spec.rb
178
+ - spec/higher_level_api/integration/basic_nack_spec.rb
179
+ - spec/higher_level_api/integration/basic_publish_spec.rb
180
+ - spec/higher_level_api/integration/basic_qos_spec.rb
181
+ - spec/higher_level_api/integration/basic_recover_spec.rb
182
+ - spec/higher_level_api/integration/basic_reject_spec.rb
183
+ - spec/higher_level_api/integration/basic_return_spec.rb
184
+ - spec/higher_level_api/integration/channel_close_spec.rb
185
+ - spec/higher_level_api/integration/channel_flow_spec.rb
186
+ - spec/higher_level_api/integration/channel_open_spec.rb
187
+ - spec/higher_level_api/integration/channel_open_stress_spec.rb
188
+ - spec/higher_level_api/integration/confirm_select_spec.rb
189
+ - spec/higher_level_api/integration/connection_spec.rb
190
+ - spec/higher_level_api/integration/consumer_cancellation_notification_spec.rb
191
+ - spec/higher_level_api/integration/dead_lettering_spec.rb
192
+ - spec/higher_level_api/integration/exchange_bind_spec.rb
193
+ - spec/higher_level_api/integration/exchange_declare_spec.rb
194
+ - spec/higher_level_api/integration/exchange_delete_spec.rb
195
+ - spec/higher_level_api/integration/exchange_unbind_spec.rb
196
+ - spec/higher_level_api/integration/message_properties_access_spec.rb
197
+ - spec/higher_level_api/integration/publisher_confirms_spec.rb
198
+ - spec/higher_level_api/integration/queue_bind_spec.rb
199
+ - spec/higher_level_api/integration/queue_declare_spec.rb
200
+ - spec/higher_level_api/integration/queue_delete_spec.rb
201
+ - spec/higher_level_api/integration/queue_purge_spec.rb
202
+ - spec/higher_level_api/integration/queue_unbind_spec.rb
203
+ - spec/higher_level_api/integration/sender_selected_distribution_spec.rb
204
+ - spec/higher_level_api/integration/tx_commit_spec.rb
205
+ - spec/higher_level_api/integration/tx_rollback_spec.rb
206
+ - spec/issues/issue78_spec.rb
207
+ - spec/issues/issue83_spec.rb
208
+ - spec/lower_level_api/integration/basic_cancel_spec.rb
209
+ - spec/lower_level_api/integration/basic_consume_spec.rb
210
+ - spec/spec_helper.rb
211
+ - spec/unit/bunny_spec.rb
212
+ - spec/unit/concurrent/condition_spec.rb
213
+ - spec/unit/transport_spec.rb