bunny 1.3.0 → 2.17.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.
Files changed (143) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE.md +18 -0
  3. data/.gitignore +7 -1
  4. data/.rspec +1 -3
  5. data/.travis.yml +21 -14
  6. data/CONTRIBUTING.md +132 -0
  7. data/ChangeLog.md +887 -1
  8. data/Gemfile +13 -13
  9. data/LICENSE +1 -1
  10. data/README.md +46 -60
  11. data/Rakefile +54 -0
  12. data/bunny.gemspec +5 -11
  13. data/docker-compose.yml +28 -0
  14. data/docker/Dockerfile +24 -0
  15. data/docker/apt/preferences.d/erlang +3 -0
  16. data/docker/apt/sources.list.d/bintray.rabbitmq.list +2 -0
  17. data/docker/docker-entrypoint.sh +26 -0
  18. data/docker/rabbitmq.conf +29 -0
  19. data/examples/connection/automatic_recovery_with_basic_get.rb +1 -1
  20. data/examples/connection/automatic_recovery_with_client_named_queues.rb +1 -1
  21. data/examples/connection/automatic_recovery_with_multiple_consumers.rb +1 -1
  22. data/examples/connection/automatic_recovery_with_republishing.rb +1 -1
  23. data/examples/connection/automatic_recovery_with_server_named_queues.rb +1 -1
  24. data/examples/connection/channel_level_exception.rb +1 -9
  25. data/examples/connection/disabled_automatic_recovery.rb +1 -1
  26. data/examples/connection/heartbeat.rb +1 -1
  27. data/examples/consumers/high_and_low_priority.rb +1 -1
  28. data/examples/guides/extensions/alternate_exchange.rb +2 -0
  29. data/examples/guides/extensions/basic_nack.rb +1 -1
  30. data/examples/guides/extensions/dead_letter_exchange.rb +1 -1
  31. data/examples/guides/getting_started/hello_world.rb +2 -0
  32. data/examples/guides/getting_started/weathr.rb +2 -0
  33. data/examples/guides/queues/one_off_consumer.rb +2 -0
  34. data/examples/guides/queues/redeliveries.rb +4 -2
  35. data/lib/bunny.rb +8 -4
  36. data/lib/bunny/channel.rb +268 -153
  37. data/lib/bunny/channel_id_allocator.rb +6 -4
  38. data/lib/bunny/concurrent/continuation_queue.rb +34 -13
  39. data/lib/bunny/consumer_work_pool.rb +34 -6
  40. data/lib/bunny/cruby/socket.rb +48 -21
  41. data/lib/bunny/cruby/ssl_socket.rb +65 -4
  42. data/lib/bunny/exceptions.rb +25 -4
  43. data/lib/bunny/exchange.rb +24 -28
  44. data/lib/bunny/get_response.rb +1 -1
  45. data/lib/bunny/heartbeat_sender.rb +3 -2
  46. data/lib/bunny/jruby/socket.rb +23 -6
  47. data/lib/bunny/jruby/ssl_socket.rb +5 -0
  48. data/lib/bunny/queue.rb +31 -22
  49. data/lib/bunny/reader_loop.rb +31 -18
  50. data/lib/bunny/session.rb +448 -159
  51. data/lib/bunny/test_kit.rb +14 -0
  52. data/lib/bunny/timeout.rb +1 -12
  53. data/lib/bunny/transport.rb +205 -98
  54. data/lib/bunny/version.rb +1 -1
  55. data/repl +1 -1
  56. data/spec/config/enabled_plugins +1 -0
  57. data/spec/config/rabbitmq.conf +13 -0
  58. data/spec/higher_level_api/integration/basic_ack_spec.rb +175 -16
  59. data/spec/higher_level_api/integration/basic_cancel_spec.rb +77 -11
  60. data/spec/higher_level_api/integration/basic_consume_spec.rb +60 -55
  61. data/spec/higher_level_api/integration/basic_consume_with_objects_spec.rb +6 -6
  62. data/spec/higher_level_api/integration/basic_get_spec.rb +31 -7
  63. data/spec/higher_level_api/integration/basic_nack_spec.rb +22 -19
  64. data/spec/higher_level_api/integration/basic_publish_spec.rb +11 -100
  65. data/spec/higher_level_api/integration/basic_qos_spec.rb +32 -4
  66. data/spec/higher_level_api/integration/basic_reject_spec.rb +94 -16
  67. data/spec/higher_level_api/integration/basic_return_spec.rb +4 -4
  68. data/spec/higher_level_api/integration/channel_close_spec.rb +51 -10
  69. data/spec/higher_level_api/integration/channel_open_spec.rb +12 -12
  70. data/spec/higher_level_api/integration/connection_recovery_spec.rb +424 -221
  71. data/spec/higher_level_api/integration/connection_spec.rb +300 -126
  72. data/spec/higher_level_api/integration/connection_stop_spec.rb +31 -19
  73. data/spec/higher_level_api/integration/consumer_cancellation_notification_spec.rb +17 -17
  74. data/spec/higher_level_api/integration/dead_lettering_spec.rb +34 -11
  75. data/spec/higher_level_api/integration/exchange_bind_spec.rb +5 -5
  76. data/spec/higher_level_api/integration/exchange_declare_spec.rb +32 -31
  77. data/spec/higher_level_api/integration/exchange_delete_spec.rb +12 -12
  78. data/spec/higher_level_api/integration/exchange_unbind_spec.rb +5 -5
  79. data/spec/higher_level_api/integration/exclusive_queue_spec.rb +5 -5
  80. data/spec/higher_level_api/integration/heartbeat_spec.rb +26 -8
  81. data/spec/higher_level_api/integration/message_properties_access_spec.rb +49 -49
  82. data/spec/higher_level_api/integration/predeclared_exchanges_spec.rb +2 -2
  83. data/spec/higher_level_api/integration/publisher_confirms_spec.rb +156 -42
  84. data/spec/higher_level_api/integration/publishing_edge_cases_spec.rb +19 -19
  85. data/spec/higher_level_api/integration/queue_bind_spec.rb +23 -23
  86. data/spec/higher_level_api/integration/queue_declare_spec.rb +129 -34
  87. data/spec/higher_level_api/integration/queue_delete_spec.rb +2 -2
  88. data/spec/higher_level_api/integration/queue_purge_spec.rb +5 -5
  89. data/spec/higher_level_api/integration/queue_unbind_spec.rb +6 -6
  90. data/spec/higher_level_api/integration/read_only_consumer_spec.rb +9 -9
  91. data/spec/higher_level_api/integration/sender_selected_distribution_spec.rb +10 -10
  92. data/spec/higher_level_api/integration/tls_connection_spec.rb +224 -89
  93. data/spec/higher_level_api/integration/toxiproxy_spec.rb +76 -0
  94. data/spec/higher_level_api/integration/tx_commit_spec.rb +1 -1
  95. data/spec/higher_level_api/integration/tx_rollback_spec.rb +1 -1
  96. data/spec/higher_level_api/integration/with_channel_spec.rb +2 -2
  97. data/spec/issues/issue100_spec.rb +11 -11
  98. data/spec/issues/issue141_spec.rb +13 -14
  99. data/spec/issues/issue202_spec.rb +1 -1
  100. data/spec/issues/issue224_spec.rb +40 -0
  101. data/spec/issues/issue465_spec.rb +32 -0
  102. data/spec/issues/issue549_spec.rb +30 -0
  103. data/spec/issues/issue78_spec.rb +21 -24
  104. data/spec/issues/issue83_spec.rb +5 -6
  105. data/spec/issues/issue97_spec.rb +44 -45
  106. data/spec/lower_level_api/integration/basic_cancel_spec.rb +15 -16
  107. data/spec/lower_level_api/integration/basic_consume_spec.rb +20 -21
  108. data/spec/spec_helper.rb +8 -26
  109. data/spec/stress/channel_close_stress_spec.rb +64 -0
  110. data/spec/stress/channel_open_stress_spec.rb +15 -9
  111. data/spec/stress/channel_open_stress_with_single_threaded_connection_spec.rb +7 -7
  112. data/spec/stress/concurrent_consumers_stress_spec.rb +18 -16
  113. data/spec/stress/concurrent_publishers_stress_spec.rb +16 -19
  114. data/spec/stress/connection_open_close_spec.rb +9 -9
  115. data/spec/stress/merry_go_round_spec.rb +105 -0
  116. data/spec/tls/client_key.pem +49 -25
  117. data/spec/tls/generate-server-cert.sh +8 -0
  118. data/spec/tls/server-openssl.cnf +10 -0
  119. data/spec/tls/server.csr +16 -0
  120. data/spec/tls/server_key.pem +49 -25
  121. data/spec/toxiproxy_helper.rb +28 -0
  122. data/spec/unit/bunny_spec.rb +5 -5
  123. data/spec/unit/concurrent/atomic_fixnum_spec.rb +6 -6
  124. data/spec/unit/concurrent/condition_spec.rb +8 -8
  125. data/spec/unit/concurrent/linked_continuation_queue_spec.rb +2 -2
  126. data/spec/unit/concurrent/synchronized_sorted_set_spec.rb +16 -16
  127. data/spec/unit/exchange_recovery_spec.rb +39 -0
  128. data/spec/unit/version_delivery_tag_spec.rb +3 -3
  129. metadata +65 -47
  130. data/.ruby-version +0 -1
  131. data/lib/bunny/compatibility.rb +0 -24
  132. data/lib/bunny/system_timer.rb +0 -20
  133. data/spec/compatibility/queue_declare_spec.rb +0 -44
  134. data/spec/compatibility/queue_declare_with_default_channel_spec.rb +0 -33
  135. data/spec/higher_level_api/integration/basic_recover_spec.rb +0 -18
  136. data/spec/higher_level_api/integration/confirm_select_spec.rb +0 -19
  137. data/spec/higher_level_api/integration/consistent_hash_exchange_spec.rb +0 -50
  138. data/spec/higher_level_api/integration/merry_go_round_spec.rb +0 -85
  139. data/spec/stress/long_running_consumer_spec.rb +0 -83
  140. data/spec/tls/cacert.pem +0 -18
  141. data/spec/tls/client_cert.pem +0 -18
  142. data/spec/tls/server_cert.pem +0 -18
  143. data/spec/unit/system_timer_spec.rb +0 -10
@@ -1,4 +1,4 @@
1
- require "bunny/compatibility"
1
+ require 'amq/protocol'
2
2
 
3
3
  module Bunny
4
4
  # Represents AMQP 0.9.1 exchanges.
@@ -7,9 +7,6 @@ module Bunny
7
7
  # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
8
8
  class Exchange
9
9
 
10
- include Bunny::Compatibility
11
-
12
-
13
10
  #
14
11
  # API
15
12
  #
@@ -33,12 +30,12 @@ module Bunny
33
30
  attr_accessor :opts
34
31
 
35
32
 
36
- # The default exchange. Default exchange is a direct exchange that is predefined.
37
- # It cannot be removed. Every queue is bind to this (direct) exchange by default with
38
- # the following routing semantics: messages will be routed to the queue withe same
39
- # same name as message's routing key. In other words, if a message is published with
40
- # a routing key of "weather.usa.ca.sandiego" and there is a queue Q with this name,
41
- # that message will be routed to Q.
33
+ # The default exchange. This exchange is a direct exchange that is predefined by the broker
34
+ # and that cannot be removed. Every queue is bound to this exchange by default with
35
+ # the following routing semantics: messages will be routed to the queue with the same
36
+ # name as the message's routing key. In other words, if a message is published with
37
+ # a routing key of "weather.usa.ca.sandiego" and there is a queue with this name,
38
+ # the message will be routed to the queue.
42
39
  #
43
40
  # @param [Bunny::Channel] channel_or_connection Channel to use. {Bunny::Session} instances
44
41
  # are only supported for backwards compatibility.
@@ -46,11 +43,11 @@ module Bunny
46
43
  # @example Publishing a messages to the tasks queue
47
44
  # channel = Bunny::Channel.new(connection)
48
45
  # tasks_queue = channel.queue("tasks")
49
- # Bunny::Exchange.default(channel).publish("make clean", routing_key => "tasks")
46
+ # Bunny::Exchange.default(channel).publish("make clean", :routing_key => "tasks")
50
47
  #
51
48
  # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
52
49
  # @see http://www.rabbitmq.com/resources/specs/amqp0-9-1.pdf AMQP 0.9.1 specification (Section 2.1.2.4)
53
- # @note Do not confuse default exchange with amq.direct: amq.direct is a pre-defined direct
50
+ # @note Do not confuse the default exchange with amq.direct: amq.direct is a pre-defined direct
54
51
  # exchange that doesn't have any special routing semantics.
55
52
  # @return [Exchange] An instance that corresponds to the default exchange (of type direct).
56
53
  # @api public
@@ -58,11 +55,10 @@ module Bunny
58
55
  self.new(channel_or_connection, :direct, AMQ::Protocol::EMPTY_STRING, :no_declare => true)
59
56
  end
60
57
 
61
- # @param [Bunny::Channel] channel_or_connection Channel this exchange will use. {Bunny::Session} instances are supported only for
62
- # backwards compatibility with 0.8.
63
- # @param [Symbol,String] type Exchange type
64
- # @param [String] name Exchange name
65
- # @param [Hash] opts Exchange properties
58
+ # @param [Bunny::Channel] channel Channel this exchange will use.
59
+ # @param [Symbol,String] type Exchange type
60
+ # @param [String] name Exchange name
61
+ # @param [Hash] opts Exchange properties
66
62
  #
67
63
  # @option opts [Boolean] :durable (false) Should this exchange be durable?
68
64
  # @option opts [Boolean] :auto_delete (false) Should this exchange be automatically deleted when it is no longer used?
@@ -75,10 +71,8 @@ module Bunny
75
71
  # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
76
72
  # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
77
73
  # @api public
78
- def initialize(channel_or_connection, type, name, opts = {})
79
- # old Bunny versions pass a connection here. In that case,
80
- # we just use default channel from it. MK.
81
- @channel = channel_from(channel_or_connection)
74
+ def initialize(channel, type, name, opts = {})
75
+ @channel = channel
82
76
  @name = name
83
77
  @type = type
84
78
  @options = self.class.add_default_options(name, opts)
@@ -88,6 +82,8 @@ module Bunny
88
82
  @internal = @options[:internal]
89
83
  @arguments = @options[:arguments]
90
84
 
85
+ @bindings = Set.new
86
+
91
87
  declare! unless opts[:no_declare] || predeclared? || (@name == AMQ::Protocol::EMPTY_STRING)
92
88
 
93
89
  @channel.register_exchange(self)
@@ -177,6 +173,7 @@ module Bunny
177
173
  # @api public
178
174
  def bind(source, opts = {})
179
175
  @channel.exchange_bind(source, self, opts)
176
+ @bindings.add(source: source, opts: opts)
180
177
 
181
178
  self
182
179
  end
@@ -197,6 +194,7 @@ module Bunny
197
194
  # @api public
198
195
  def unbind(source, opts = {})
199
196
  @channel.exchange_unbind(source, self, opts)
197
+ @bindings.delete(source: source, opts: opts)
200
198
 
201
199
  self
202
200
  end
@@ -222,8 +220,11 @@ module Bunny
222
220
 
223
221
  # @private
224
222
  def recover_from_network_failure
225
- # puts "Recovering exchange #{@name} from network failure"
226
- declare! unless predefined?
223
+ declare! unless @options[:no_declare] ||predefined?
224
+
225
+ @bindings.each do |b|
226
+ bind(b[:source], b[:opts])
227
+ end
227
228
  end
228
229
 
229
230
 
@@ -253,11 +254,6 @@ module Bunny
253
254
  @channel.exchange_declare(@name, @type, @options)
254
255
  end
255
256
 
256
- # @private
257
- def self.add_default_options(name, opts, block)
258
- { :exchange => name, :nowait => (block.nil? && !name.empty?) }.merge(opts)
259
- end
260
-
261
257
  # @private
262
258
  def self.add_default_options(name, opts)
263
259
  # :nowait is always false for Bunny
@@ -20,7 +20,7 @@ module Bunny
20
20
  attr_reader :channel
21
21
 
22
22
  # @private
23
- def initialize(get_ok, consumer, channel)
23
+ def initialize(get_ok, channel)
24
24
  @get_ok = get_ok
25
25
  @hash = {
26
26
  :delivery_tag => @get_ok.delivery_tag,
@@ -29,6 +29,7 @@ module Bunny
29
29
  @interval = [(period / 2) - 1, 0.4].max
30
30
 
31
31
  @thread = Thread.new(&method(:run))
32
+ @thread.report_on_exception = false if @thread.respond_to?(:report_on_exception)
32
33
  end
33
34
  end
34
35
 
@@ -62,8 +63,8 @@ module Bunny
62
63
  now = Time.now
63
64
 
64
65
  if now > (@last_activity_time + @interval)
65
- @logger.debug "Sending a heartbeat, last activity time: #{@last_activity_time}, interval (s): #{@interval}"
66
- @transport.write_without_timeout(AMQ::Protocol::HeartbeatFrame.encode)
66
+ @logger.debug { "Sending a heartbeat, last activity time: #{@last_activity_time}, interval (s): #{@interval}" }
67
+ @transport.write_without_timeout(AMQ::Protocol::HeartbeatFrame.encode, true)
67
68
  end
68
69
  end
69
70
  end
@@ -5,7 +5,22 @@ module Bunny
5
5
  # TCP socket extension that uses Socket#readpartial to avoid excessive CPU
6
6
  # burn after some time. See issue #165.
7
7
  # @private
8
- class Socket < Bunny::Socket
8
+ module Socket
9
+ include Bunny::Socket
10
+
11
+ def self.open(host, port, options = {})
12
+ socket = ::Socket.tcp(host, port, nil, nil,
13
+ connect_timeout: options[:connect_timeout])
14
+ if ::Socket.constants.include?('TCP_NODELAY') || ::Socket.constants.include?(:TCP_NODELAY)
15
+ socket.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, true)
16
+ end
17
+ socket.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true) if options.fetch(:keepalive, true)
18
+ socket.extend self
19
+ socket.options = { :host => host, :port => port }.merge(options)
20
+ socket
21
+ rescue Errno::ETIMEDOUT
22
+ raise ClientTimeout
23
+ end
9
24
 
10
25
  # Reads given number of bytes with an optional timeout
11
26
  #
@@ -15,17 +30,17 @@ module Bunny
15
30
  # @return [String] Data read from the socket
16
31
  # @api public
17
32
  def read_fully(count, timeout = nil)
18
- return nil if @__bunny_socket_eof_flag__
19
-
20
33
  value = ''
34
+
21
35
  begin
22
36
  loop do
23
- value << readpartial(count - value.bytesize)
37
+ value << read_nonblock(count - value.bytesize)
24
38
  break if value.bytesize >= count
25
39
  end
26
40
  rescue EOFError
27
- # @eof will break Rubinius' TCPSocket implementation. MK.
28
- @__bunny_socket_eof_flag__ = true
41
+ # JRuby specific fix via https://github.com/jruby/jruby/issues/1694#issuecomment-54873532
42
+ IO.select([self], nil, nil, timeout)
43
+ retry
29
44
  rescue *READ_RETRY_EXCEPTION_CLASSES
30
45
  if IO.select([self], nil, nil, timeout)
31
46
  retry
@@ -33,8 +48,10 @@ module Bunny
33
48
  raise Timeout::Error, "IO timeout when reading #{count} bytes"
34
49
  end
35
50
  end
51
+
36
52
  value
37
53
  end # read_fully
54
+
38
55
  end
39
56
  end
40
57
  end
@@ -8,6 +8,11 @@ module Bunny
8
8
  # methods found in Bunny::Socket.
9
9
  class SSLSocket < Bunny::SSLSocket
10
10
 
11
+ def initialize(*args)
12
+ super
13
+ @__bunny_socket_eof_flag__ = false
14
+ end
15
+
11
16
  # Reads given number of bytes with an optional timeout
12
17
  #
13
18
  # @param [Integer] count How many bytes to read
data/lib/bunny/queue.rb CHANGED
@@ -1,4 +1,3 @@
1
- require "bunny/compatibility"
2
1
  require "bunny/get_response"
3
2
 
4
3
  module Bunny
@@ -8,9 +7,6 @@ module Bunny
8
7
  # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
9
8
  class Queue
10
9
 
11
- include Bunny::Compatibility
12
-
13
-
14
10
  #
15
11
  # API
16
12
  #
@@ -22,8 +18,7 @@ module Bunny
22
18
  # @return [Hash] Options this queue was created with
23
19
  attr_reader :options
24
20
 
25
- # @param [Bunny::Channel] channel_or_connection Channel this queue will use. {Bunny::Session} instances are supported only for
26
- # backwards compatibility with 0.8.
21
+ # @param [Bunny::Channel] channel Channel this queue will use.
27
22
  # @param [String] name Queue name. Pass an empty string to make RabbitMQ generate a unique one.
28
23
  # @param [Hash] opts Queue properties
29
24
  #
@@ -36,13 +31,12 @@ module Bunny
36
31
  # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
37
32
  # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
38
33
  # @api public
39
- def initialize(channel_or_connection, name = AMQ::Protocol::EMPTY_STRING, opts = {})
34
+ def initialize(channel, name = AMQ::Protocol::EMPTY_STRING, opts = {})
40
35
  # old Bunny versions pass a connection here. In that case,
41
36
  # we just use default channel from it. MK.
42
- @channel = channel_from(channel_or_connection)
37
+ @channel = channel
43
38
  @name = name
44
39
  @options = self.class.add_default_options(name, opts)
45
- @consumers = Hash.new
46
40
 
47
41
  @durable = @options[:durable]
48
42
  @exclusive = @options[:exclusive]
@@ -93,6 +87,15 @@ module Bunny
93
87
  @arguments
94
88
  end
95
89
 
90
+ def to_s
91
+ oid = ("0x%x" % (self.object_id << 1))
92
+ "<#{self.class.name}:#{oid} @name=\"#{name}\" channel=#{@channel.to_s} @durable=#{@durable} @auto_delete=#{@auto_delete} @exclusive=#{@exclusive} @arguments=#{@arguments}>"
93
+ end
94
+
95
+ def inspect
96
+ to_s
97
+ end
98
+
96
99
  # Binds queue to an exchange
97
100
  #
98
101
  # @param [Bunny::Exchange,String] exchange Exchange to bind to
@@ -152,9 +155,9 @@ module Bunny
152
155
  #
153
156
  # @param [Hash] opts Options
154
157
  #
158
+ # @option opts [Boolean] :ack (false) [DEPRECATED] Use :manual_ack instead
155
159
  # @option opts [Boolean] :manual_ack (false) Will this consumer use manual acknowledgements?
156
160
  # @option opts [Boolean] :exclusive (false) Should this consumer be exclusive for this queue?
157
- # @option opts [Boolean] :block (false) Should the call block calling thread?
158
161
  # @option opts [#call] :on_cancellation Block to execute when this consumer is cancelled remotely (e.g. via the RabbitMQ Management plugin)
159
162
  # @option opts [String] :consumer_tag Unique consumer identifier. It is usually recommended to let Bunny generate it for you.
160
163
  # @option opts [Hash] :arguments ({}) Additional (optional) arguments, typically used by RabbitMQ extensions
@@ -163,17 +166,22 @@ module Bunny
163
166
  # @api public
164
167
  def subscribe(opts = {
165
168
  :consumer_tag => @channel.generate_consumer_tag,
166
- :ack => false,
169
+ :manual_ack => false,
167
170
  :exclusive => false,
168
171
  :block => false,
169
172
  :on_cancellation => nil
170
173
  }, &block)
171
174
 
175
+ unless opts[:ack].nil?
176
+ warn "[DEPRECATION] `:ack` is deprecated. Please use `:manual_ack` instead."
177
+ opts[:manual_ack] = opts[:ack]
178
+ end
179
+
172
180
  ctag = opts.fetch(:consumer_tag, @channel.generate_consumer_tag)
173
181
  consumer = Consumer.new(@channel,
174
182
  self,
175
183
  ctag,
176
- !(opts[:ack] || opts[:manual_ack]),
184
+ !opts[:manual_ack],
177
185
  opts[:exclusive],
178
186
  opts[:arguments])
179
187
 
@@ -208,7 +216,8 @@ module Bunny
208
216
 
209
217
  # @param [Hash] opts Options
210
218
  #
211
- # @option opts [Boolean] :ack (false) Will the message be acknowledged manually?
219
+ # @option opts [Boolean] :ack (false) [DEPRECATED] Use :manual_ack instead
220
+ # @option opts [Boolean] :manual_ack (false) Will the message be acknowledged manually?
212
221
  #
213
222
  # @return [Array] Triple of delivery info, message properties and message content.
214
223
  # If the queue is empty, all three will be nils.
@@ -229,12 +238,17 @@ module Bunny
229
238
  #
230
239
  # puts "This is the message: " + payload + "\n\n"
231
240
  # conn.close
232
- def pop(opts = {:ack => false}, &block)
241
+ def pop(opts = {:manual_ack => false}, &block)
242
+ unless opts[:ack].nil?
243
+ warn "[DEPRECATION] `:ack` is deprecated. Please use `:manual_ack` instead."
244
+ opts[:manual_ack] = opts[:ack]
245
+ end
246
+
233
247
  get_response, properties, content = @channel.basic_get(@name, opts)
234
248
 
235
249
  if block
236
250
  if properties
237
- di = GetResponse.new(get_response, properties, @channel)
251
+ di = GetResponse.new(get_response, @channel)
238
252
  mp = MessageProperties.new(properties)
239
253
 
240
254
  block.call(di, mp, content)
@@ -243,7 +257,7 @@ module Bunny
243
257
  end
244
258
  else
245
259
  if properties
246
- di = GetResponse.new(get_response, properties, @channel)
260
+ di = GetResponse.new(get_response, @channel)
247
261
  mp = MessageProperties.new(properties)
248
262
  [di, mp, content]
249
263
  else
@@ -326,7 +340,7 @@ module Bunny
326
340
  # TODO: inject and use logger
327
341
  # puts "Recovering queue #{@name}"
328
342
  begin
329
- declare!
343
+ declare! unless @options[:no_declare]
330
344
 
331
345
  @channel.register_queue(self)
332
346
  rescue Exception => e
@@ -358,11 +372,6 @@ module Bunny
358
372
 
359
373
  protected
360
374
 
361
- # @private
362
- def self.add_default_options(name, opts, block)
363
- { :queue => name, :nowait => (block.nil? && !name.empty?) }.merge(opts)
364
- end
365
-
366
375
  # @private
367
376
  def self.add_default_options(name, opts)
368
377
  # :nowait is always false for Bunny
@@ -9,13 +9,17 @@ module Bunny
9
9
  # @private
10
10
  class ReaderLoop
11
11
 
12
- def initialize(transport, session, session_thread)
13
- @transport = transport
14
- @session = session
15
- @session_thread = session_thread
16
- @logger = @session.logger
12
+ def initialize(transport, session, session_error_handler)
13
+ @transport = transport
14
+ @session = session
15
+ @session_error_handler = session_error_handler
16
+ @logger = @session.logger
17
17
 
18
- @mutex = Mutex.new
18
+ @mutex = Mutex.new
19
+
20
+ @stopping = false
21
+ @stopped = false
22
+ @network_is_down = false
19
23
  end
20
24
 
21
25
 
@@ -33,15 +37,17 @@ module Bunny
33
37
  begin
34
38
  break if @mutex.synchronize { @stopping || @stopped || @network_is_down }
35
39
  run_once
36
- rescue AMQ::Protocol::EmptyResponseError, IOError, SystemCallError => e
40
+ rescue AMQ::Protocol::EmptyResponseError, IOError, SystemCallError, Timeout::Error,
41
+ OpenSSL::OpenSSLError => e
37
42
  break if terminate? || @session.closing? || @session.closed?
38
43
 
39
- log_exception(e)
40
44
  @network_is_down = true
41
45
  if @session.automatically_recover?
46
+ log_exception(e, level: :warn)
42
47
  @session.handle_network_failure(e)
43
48
  else
44
- @session_thread.raise(Bunny::NetworkFailure.new("detected a network failure: #{e.message}", e))
49
+ log_exception(e)
50
+ @session_error_handler.raise(Bunny::NetworkFailure.new("detected a network failure: #{e.message}", e))
45
51
  end
46
52
  rescue ShutdownSignal => _
47
53
  @mutex.synchronize { @stopping = true }
@@ -52,9 +58,9 @@ module Bunny
52
58
  log_exception(e)
53
59
 
54
60
  @network_is_down = true
55
- @session_thread.raise(Bunny::NetworkFailure.new("caught an unexpected exception in the network loop: #{e.message}", e))
61
+ @session_error_handler.raise(Bunny::NetworkFailure.new("caught an unexpected exception in the network loop: #{e.message}", e))
56
62
  end
57
- rescue Errno::EBADF => ebadf
63
+ rescue Errno::EBADF => _ebadf
58
64
  break if terminate?
59
65
  # ignored, happens when we loop after the transport has already been closed
60
66
  @mutex.synchronize { @stopping = true }
@@ -92,11 +98,11 @@ module Bunny
92
98
  end
93
99
 
94
100
  def stopped?
95
- @mutex.synchronize { @stopped = true }
101
+ @mutex.synchronize { @stopped }
96
102
  end
97
103
 
98
104
  def stopping?
99
- @mutex.synchronize { @stopping = true }
105
+ @mutex.synchronize { @stopping }
100
106
  end
101
107
 
102
108
  def terminate_with(e)
@@ -110,7 +116,14 @@ module Bunny
110
116
  end
111
117
 
112
118
  def join
113
- @thread.join if @thread
119
+ # Thread#join can/would trigger a re-raise of an unhandled exception in this thread.
120
+ # In addition, Thread.handle_interrupt can be used by other libraries or application code
121
+ # that would make this join operation fail with an obscure exception.
122
+ # So we try to save everyone some really unpleasant debugging time by introducing
123
+ # this condition which typically would not evaluate to true anyway.
124
+ #
125
+ # See ruby-amqp/bunny#589 and ruby-amqp/bunny#590 for background.
126
+ @thread.join if @thread && @thread != Thread.current
114
127
  end
115
128
 
116
129
  def kill
@@ -122,12 +135,12 @@ module Bunny
122
135
 
123
136
  protected
124
137
 
125
- def log_exception(e)
138
+ def log_exception(e, level: :error)
126
139
  if !(io_error?(e) && (@session.closing? || @session.closed?))
127
- @logger.error "Exception in the reader loop: #{e.class.name}: #{e.message}"
128
- @logger.error "Backtrace: "
140
+ @logger.send level, "Exception in the reader loop: #{e.class.name}: #{e.message}"
141
+ @logger.send level, "Backtrace: "
129
142
  e.backtrace.each do |line|
130
- @logger.error "\t#{line}"
143
+ @logger.send level, "\t#{line}"
131
144
  end
132
145
  end
133
146
  end