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.
- checksums.yaml +5 -5
- data/.github/ISSUE_TEMPLATE.md +18 -0
- data/.gitignore +7 -1
- data/.rspec +1 -3
- data/.travis.yml +21 -14
- data/CONTRIBUTING.md +132 -0
- data/ChangeLog.md +887 -1
- data/Gemfile +13 -13
- data/LICENSE +1 -1
- data/README.md +46 -60
- data/Rakefile +54 -0
- data/bunny.gemspec +5 -11
- data/docker-compose.yml +28 -0
- data/docker/Dockerfile +24 -0
- data/docker/apt/preferences.d/erlang +3 -0
- data/docker/apt/sources.list.d/bintray.rabbitmq.list +2 -0
- data/docker/docker-entrypoint.sh +26 -0
- data/docker/rabbitmq.conf +29 -0
- data/examples/connection/automatic_recovery_with_basic_get.rb +1 -1
- data/examples/connection/automatic_recovery_with_client_named_queues.rb +1 -1
- data/examples/connection/automatic_recovery_with_multiple_consumers.rb +1 -1
- data/examples/connection/automatic_recovery_with_republishing.rb +1 -1
- data/examples/connection/automatic_recovery_with_server_named_queues.rb +1 -1
- data/examples/connection/channel_level_exception.rb +1 -9
- data/examples/connection/disabled_automatic_recovery.rb +1 -1
- data/examples/connection/heartbeat.rb +1 -1
- data/examples/consumers/high_and_low_priority.rb +1 -1
- data/examples/guides/extensions/alternate_exchange.rb +2 -0
- data/examples/guides/extensions/basic_nack.rb +1 -1
- data/examples/guides/extensions/dead_letter_exchange.rb +1 -1
- data/examples/guides/getting_started/hello_world.rb +2 -0
- data/examples/guides/getting_started/weathr.rb +2 -0
- data/examples/guides/queues/one_off_consumer.rb +2 -0
- data/examples/guides/queues/redeliveries.rb +4 -2
- data/lib/bunny.rb +8 -4
- data/lib/bunny/channel.rb +268 -153
- data/lib/bunny/channel_id_allocator.rb +6 -4
- data/lib/bunny/concurrent/continuation_queue.rb +34 -13
- data/lib/bunny/consumer_work_pool.rb +34 -6
- data/lib/bunny/cruby/socket.rb +48 -21
- data/lib/bunny/cruby/ssl_socket.rb +65 -4
- data/lib/bunny/exceptions.rb +25 -4
- data/lib/bunny/exchange.rb +24 -28
- data/lib/bunny/get_response.rb +1 -1
- data/lib/bunny/heartbeat_sender.rb +3 -2
- data/lib/bunny/jruby/socket.rb +23 -6
- data/lib/bunny/jruby/ssl_socket.rb +5 -0
- data/lib/bunny/queue.rb +31 -22
- data/lib/bunny/reader_loop.rb +31 -18
- data/lib/bunny/session.rb +448 -159
- data/lib/bunny/test_kit.rb +14 -0
- data/lib/bunny/timeout.rb +1 -12
- data/lib/bunny/transport.rb +205 -98
- data/lib/bunny/version.rb +1 -1
- data/repl +1 -1
- data/spec/config/enabled_plugins +1 -0
- data/spec/config/rabbitmq.conf +13 -0
- data/spec/higher_level_api/integration/basic_ack_spec.rb +175 -16
- data/spec/higher_level_api/integration/basic_cancel_spec.rb +77 -11
- data/spec/higher_level_api/integration/basic_consume_spec.rb +60 -55
- data/spec/higher_level_api/integration/basic_consume_with_objects_spec.rb +6 -6
- data/spec/higher_level_api/integration/basic_get_spec.rb +31 -7
- data/spec/higher_level_api/integration/basic_nack_spec.rb +22 -19
- data/spec/higher_level_api/integration/basic_publish_spec.rb +11 -100
- data/spec/higher_level_api/integration/basic_qos_spec.rb +32 -4
- data/spec/higher_level_api/integration/basic_reject_spec.rb +94 -16
- data/spec/higher_level_api/integration/basic_return_spec.rb +4 -4
- data/spec/higher_level_api/integration/channel_close_spec.rb +51 -10
- data/spec/higher_level_api/integration/channel_open_spec.rb +12 -12
- data/spec/higher_level_api/integration/connection_recovery_spec.rb +424 -221
- data/spec/higher_level_api/integration/connection_spec.rb +300 -126
- data/spec/higher_level_api/integration/connection_stop_spec.rb +31 -19
- data/spec/higher_level_api/integration/consumer_cancellation_notification_spec.rb +17 -17
- data/spec/higher_level_api/integration/dead_lettering_spec.rb +34 -11
- data/spec/higher_level_api/integration/exchange_bind_spec.rb +5 -5
- data/spec/higher_level_api/integration/exchange_declare_spec.rb +32 -31
- data/spec/higher_level_api/integration/exchange_delete_spec.rb +12 -12
- data/spec/higher_level_api/integration/exchange_unbind_spec.rb +5 -5
- data/spec/higher_level_api/integration/exclusive_queue_spec.rb +5 -5
- data/spec/higher_level_api/integration/heartbeat_spec.rb +26 -8
- data/spec/higher_level_api/integration/message_properties_access_spec.rb +49 -49
- data/spec/higher_level_api/integration/predeclared_exchanges_spec.rb +2 -2
- data/spec/higher_level_api/integration/publisher_confirms_spec.rb +156 -42
- data/spec/higher_level_api/integration/publishing_edge_cases_spec.rb +19 -19
- data/spec/higher_level_api/integration/queue_bind_spec.rb +23 -23
- data/spec/higher_level_api/integration/queue_declare_spec.rb +129 -34
- data/spec/higher_level_api/integration/queue_delete_spec.rb +2 -2
- data/spec/higher_level_api/integration/queue_purge_spec.rb +5 -5
- data/spec/higher_level_api/integration/queue_unbind_spec.rb +6 -6
- data/spec/higher_level_api/integration/read_only_consumer_spec.rb +9 -9
- data/spec/higher_level_api/integration/sender_selected_distribution_spec.rb +10 -10
- data/spec/higher_level_api/integration/tls_connection_spec.rb +224 -89
- data/spec/higher_level_api/integration/toxiproxy_spec.rb +76 -0
- data/spec/higher_level_api/integration/tx_commit_spec.rb +1 -1
- data/spec/higher_level_api/integration/tx_rollback_spec.rb +1 -1
- data/spec/higher_level_api/integration/with_channel_spec.rb +2 -2
- data/spec/issues/issue100_spec.rb +11 -11
- data/spec/issues/issue141_spec.rb +13 -14
- data/spec/issues/issue202_spec.rb +1 -1
- data/spec/issues/issue224_spec.rb +40 -0
- data/spec/issues/issue465_spec.rb +32 -0
- data/spec/issues/issue549_spec.rb +30 -0
- data/spec/issues/issue78_spec.rb +21 -24
- data/spec/issues/issue83_spec.rb +5 -6
- data/spec/issues/issue97_spec.rb +44 -45
- data/spec/lower_level_api/integration/basic_cancel_spec.rb +15 -16
- data/spec/lower_level_api/integration/basic_consume_spec.rb +20 -21
- data/spec/spec_helper.rb +8 -26
- data/spec/stress/channel_close_stress_spec.rb +64 -0
- data/spec/stress/channel_open_stress_spec.rb +15 -9
- data/spec/stress/channel_open_stress_with_single_threaded_connection_spec.rb +7 -7
- data/spec/stress/concurrent_consumers_stress_spec.rb +18 -16
- data/spec/stress/concurrent_publishers_stress_spec.rb +16 -19
- data/spec/stress/connection_open_close_spec.rb +9 -9
- data/spec/stress/merry_go_round_spec.rb +105 -0
- data/spec/tls/client_key.pem +49 -25
- data/spec/tls/generate-server-cert.sh +8 -0
- data/spec/tls/server-openssl.cnf +10 -0
- data/spec/tls/server.csr +16 -0
- data/spec/tls/server_key.pem +49 -25
- data/spec/toxiproxy_helper.rb +28 -0
- data/spec/unit/bunny_spec.rb +5 -5
- data/spec/unit/concurrent/atomic_fixnum_spec.rb +6 -6
- data/spec/unit/concurrent/condition_spec.rb +8 -8
- data/spec/unit/concurrent/linked_continuation_queue_spec.rb +2 -2
- data/spec/unit/concurrent/synchronized_sorted_set_spec.rb +16 -16
- data/spec/unit/exchange_recovery_spec.rb +39 -0
- data/spec/unit/version_delivery_tag_spec.rb +3 -3
- metadata +65 -47
- data/.ruby-version +0 -1
- data/lib/bunny/compatibility.rb +0 -24
- data/lib/bunny/system_timer.rb +0 -20
- data/spec/compatibility/queue_declare_spec.rb +0 -44
- data/spec/compatibility/queue_declare_with_default_channel_spec.rb +0 -33
- data/spec/higher_level_api/integration/basic_recover_spec.rb +0 -18
- data/spec/higher_level_api/integration/confirm_select_spec.rb +0 -19
- data/spec/higher_level_api/integration/consistent_hash_exchange_spec.rb +0 -50
- data/spec/higher_level_api/integration/merry_go_round_spec.rb +0 -85
- data/spec/stress/long_running_consumer_spec.rb +0 -83
- data/spec/tls/cacert.pem +0 -18
- data/spec/tls/client_cert.pem +0 -18
- data/spec/tls/server_cert.pem +0 -18
- data/spec/unit/system_timer_spec.rb +0 -10
@@ -8,17 +8,9 @@ $:.unshift(File.expand_path("../../../lib", __FILE__))
|
|
8
8
|
|
9
9
|
require 'bunny'
|
10
10
|
|
11
|
-
conn = Bunny.new(:
|
11
|
+
conn = Bunny.new(heartbeat_timeout: 8)
|
12
12
|
conn.start
|
13
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
14
|
begin
|
23
15
|
ch2 = conn.create_channel
|
24
16
|
q = "bunny.examples.recovery.q#{rand}"
|
@@ -8,7 +8,7 @@ $:.unshift(File.expand_path("../../../lib", __FILE__))
|
|
8
8
|
|
9
9
|
require 'bunny'
|
10
10
|
|
11
|
-
conn = Bunny.new(:
|
11
|
+
conn = Bunny.new(heartbeat_timeout: 8, automatically_recover: false)
|
12
12
|
conn.start
|
13
13
|
|
14
14
|
ch = conn.create_channel
|
@@ -11,7 +11,7 @@ require 'bunny'
|
|
11
11
|
HIGH_PRIORITY_Q = "bunny.examples.priority.hilo.high"
|
12
12
|
LOW_PRIORITY_Q = "bunny.examples.priority.hilo.low"
|
13
13
|
|
14
|
-
conn = Bunny.new(:
|
14
|
+
conn = Bunny.new(heartbeat_timeout: 8)
|
15
15
|
conn.start
|
16
16
|
|
17
17
|
ch1 = conn.create_channel
|
@@ -20,7 +20,7 @@ dlq = ch.queue("", :exclusive => true).bind(dlx)
|
|
20
20
|
x.publish("")
|
21
21
|
sleep 0.2
|
22
22
|
|
23
|
-
delivery_info, _, _ = q.pop(:
|
23
|
+
delivery_info, _, _ = q.pop(:manual_ack => true)
|
24
24
|
puts "#{dlq.message_count} messages dead lettered so far"
|
25
25
|
puts "Rejecting a message"
|
26
26
|
ch.nack(delivery_info.delivery_tag)
|
@@ -4,6 +4,8 @@
|
|
4
4
|
require "rubygems"
|
5
5
|
require "bunny"
|
6
6
|
|
7
|
+
STDOUT.sync = true
|
8
|
+
|
7
9
|
puts "=> Subscribing for messages using explicit acknowledgements model"
|
8
10
|
puts
|
9
11
|
|
@@ -29,7 +31,7 @@ x = ch3.direct("amq.direct")
|
|
29
31
|
q1 = ch1.queue("bunny.examples.acknowledgements.explicit", :auto_delete => false)
|
30
32
|
q1.purge
|
31
33
|
|
32
|
-
q1.bind(x).subscribe(:
|
34
|
+
q1.bind(x).subscribe(:manual_ack => true, :block => false) do |delivery_info, properties, payload|
|
33
35
|
# do some work
|
34
36
|
sleep(0.2)
|
35
37
|
|
@@ -46,7 +48,7 @@ q1.bind(x).subscribe(:ack => true, :block => false) do |delivery_info, propertie
|
|
46
48
|
end
|
47
49
|
|
48
50
|
q2 = ch2.queue("bunny.examples.acknowledgements.explicit", :auto_delete => false)
|
49
|
-
q2.bind(x).subscribe(:
|
51
|
+
q2.bind(x).subscribe(:manual_ack => true, :block => false) do |delivery_info, properties, payload|
|
50
52
|
# do some work
|
51
53
|
sleep(0.2)
|
52
54
|
|
data/lib/bunny.rb
CHANGED
@@ -17,7 +17,7 @@ begin
|
|
17
17
|
require "openssl"
|
18
18
|
|
19
19
|
require "bunny/ssl_socket"
|
20
|
-
rescue LoadError
|
20
|
+
rescue LoadError
|
21
21
|
# no-op
|
22
22
|
end
|
23
23
|
|
@@ -50,7 +50,7 @@ module Bunny
|
|
50
50
|
AMQ::Protocol::PROTOCOL_VERSION
|
51
51
|
end
|
52
52
|
|
53
|
-
# Instantiates a new connection. The actual
|
53
|
+
# Instantiates a new connection. The actual network
|
54
54
|
# connection is started with {Bunny::Session#start}
|
55
55
|
#
|
56
56
|
# @return [Bunny::Session]
|
@@ -58,7 +58,7 @@ module Bunny
|
|
58
58
|
# @see http://rubybunny.info/articles/getting_started.html
|
59
59
|
# @see http://rubybunny.info/articles/connecting.html
|
60
60
|
# @api public
|
61
|
-
def self.new(connection_string_or_opts =
|
61
|
+
def self.new(connection_string_or_opts = ENV['RABBITMQ_URL'], opts = {})
|
62
62
|
if connection_string_or_opts.respond_to?(:keys) && opts.empty?
|
63
63
|
opts = connection_string_or_opts
|
64
64
|
end
|
@@ -70,9 +70,13 @@ module Bunny
|
|
70
70
|
end
|
71
71
|
|
72
72
|
|
73
|
-
def self.run(connection_string_or_opts =
|
73
|
+
def self.run(connection_string_or_opts = ENV['RABBITMQ_URL'], opts = {}, &block)
|
74
74
|
raise ArgumentError, 'Bunny#run requires a block' unless block
|
75
75
|
|
76
|
+
if connection_string_or_opts.respond_to?(:keys) && opts.empty?
|
77
|
+
opts = connection_string_or_opts
|
78
|
+
end
|
79
|
+
|
76
80
|
client = Session.new(connection_string_or_opts, opts)
|
77
81
|
|
78
82
|
begin
|
data/lib/bunny/channel.rb
CHANGED
@@ -37,11 +37,10 @@ module Bunny
|
|
37
37
|
# Channels can be opened either via `Bunny::Session#create_channel` (sufficient in the majority
|
38
38
|
# of cases) or by instantiating `Bunny::Channel` directly:
|
39
39
|
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
# conn.start
|
40
|
+
# conn = Bunny.new
|
41
|
+
# conn.start
|
43
42
|
#
|
44
|
-
#
|
43
|
+
# ch = conn.create_channel
|
45
44
|
#
|
46
45
|
# This will automatically allocate a channel id.
|
47
46
|
#
|
@@ -51,10 +50,8 @@ module Bunny
|
|
51
50
|
# closed, too. Closed channels can no longer be used. Attempts to use them will raise
|
52
51
|
# {Bunny::ChannelAlreadyClosed}.
|
53
52
|
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
# ch = conn.create_channel
|
57
|
-
# ch.close
|
53
|
+
# ch = conn.create_channel
|
54
|
+
# ch.close
|
58
55
|
#
|
59
56
|
# ## Higher-level API
|
60
57
|
#
|
@@ -159,6 +156,8 @@ module Bunny
|
|
159
156
|
|
160
157
|
# @return [Integer] active basic.qos prefetch value
|
161
158
|
attr_reader :prefetch_count
|
159
|
+
# @return [Integer] active basic.qos prefetch global mode
|
160
|
+
attr_reader :prefetch_global
|
162
161
|
|
163
162
|
DEFAULT_CONTENT_TYPE = "application/octet-stream".freeze
|
164
163
|
SHORTSTR_LIMIT = 255
|
@@ -170,6 +169,17 @@ module Bunny
|
|
170
169
|
@connection = connection
|
171
170
|
@logger = connection.logger
|
172
171
|
@id = id || @connection.next_channel_id
|
172
|
+
|
173
|
+
# channel allocator is exhausted
|
174
|
+
if @id < 0
|
175
|
+
msg = "Cannot open a channel: max number of channels on connection reached. Connection channel_max value: #{@connection.channel_max}"
|
176
|
+
@logger.error(msg)
|
177
|
+
|
178
|
+
raise msg
|
179
|
+
else
|
180
|
+
@logger.debug { "Allocated channel id: #{@id}" }
|
181
|
+
end
|
182
|
+
|
173
183
|
@status = :opening
|
174
184
|
|
175
185
|
@connection.register_channel(self)
|
@@ -183,6 +193,9 @@ module Bunny
|
|
183
193
|
@publishing_mutex = @connection.mutex_impl.new
|
184
194
|
@consumer_mutex = @connection.mutex_impl.new
|
185
195
|
|
196
|
+
@queue_mutex = @connection.mutex_impl.new
|
197
|
+
@exchange_mutex = @connection.mutex_impl.new
|
198
|
+
|
186
199
|
@unconfirmed_set_mutex = @connection.mutex_impl.new
|
187
200
|
|
188
201
|
self.reset_continuations
|
@@ -195,18 +208,19 @@ module Bunny
|
|
195
208
|
@threads_waiting_on_basic_get_continuations = Set.new
|
196
209
|
|
197
210
|
@next_publish_seq_no = 0
|
211
|
+
@delivery_tag_offset = 0
|
198
212
|
|
199
213
|
@recoveries_counter = Bunny::Concurrent::AtomicFixnum.new(0)
|
200
214
|
@uncaught_exception_handler = Proc.new do |e, consumer|
|
201
|
-
@logger.error "Uncaught exception from consumer #{consumer.to_s}: #{e.
|
215
|
+
@logger.error "Uncaught exception from consumer #{consumer.to_s}: #{e.inspect} @ #{e.backtrace[0]}"
|
202
216
|
end
|
203
217
|
end
|
204
218
|
|
205
219
|
attr_reader :recoveries_counter
|
206
220
|
|
207
221
|
# @private
|
208
|
-
def
|
209
|
-
@connection.
|
222
|
+
def wait_on_continuations_timeout
|
223
|
+
@connection.transport_write_timeout
|
210
224
|
end
|
211
225
|
|
212
226
|
# Opens the channel and resets its internal state
|
@@ -230,8 +244,12 @@ module Bunny
|
|
230
244
|
# {Bunny::Queue}, {Bunny::Exchange} and {Bunny::Consumer} instances.
|
231
245
|
# @api public
|
232
246
|
def close
|
247
|
+
# see bunny#528
|
248
|
+
raise_if_no_longer_open!
|
249
|
+
|
233
250
|
@connection.close_channel(self)
|
234
|
-
closed
|
251
|
+
@status = :closed
|
252
|
+
@work_pool.shutdown
|
235
253
|
maybe_kill_consumer_work_pool!
|
236
254
|
end
|
237
255
|
|
@@ -247,7 +265,6 @@ module Bunny
|
|
247
265
|
@status == :closed
|
248
266
|
end
|
249
267
|
|
250
|
-
|
251
268
|
#
|
252
269
|
# @group Backwards compatibility with 0.8.0
|
253
270
|
#
|
@@ -296,7 +313,7 @@ module Bunny
|
|
296
313
|
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
|
297
314
|
# @api public
|
298
315
|
def fanout(name, opts = {})
|
299
|
-
Exchange.new(self, :fanout, name, opts)
|
316
|
+
find_exchange(name) || Exchange.new(self, :fanout, name, opts)
|
300
317
|
end
|
301
318
|
|
302
319
|
# Declares a direct exchange or looks it up in the cache of previously
|
@@ -314,7 +331,7 @@ module Bunny
|
|
314
331
|
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
|
315
332
|
# @api public
|
316
333
|
def direct(name, opts = {})
|
317
|
-
Exchange.new(self, :direct, name, opts)
|
334
|
+
find_exchange(name) || Exchange.new(self, :direct, name, opts)
|
318
335
|
end
|
319
336
|
|
320
337
|
# Declares a topic exchange or looks it up in the cache of previously
|
@@ -332,7 +349,7 @@ module Bunny
|
|
332
349
|
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
|
333
350
|
# @api public
|
334
351
|
def topic(name, opts = {})
|
335
|
-
Exchange.new(self, :topic, name, opts)
|
352
|
+
find_exchange(name) || Exchange.new(self, :topic, name, opts)
|
336
353
|
end
|
337
354
|
|
338
355
|
# Declares a headers exchange or looks it up in the cache of previously
|
@@ -350,14 +367,14 @@ module Bunny
|
|
350
367
|
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
|
351
368
|
# @api public
|
352
369
|
def headers(name, opts = {})
|
353
|
-
Exchange.new(self, :headers, name, opts)
|
370
|
+
find_exchange(name) || Exchange.new(self, :headers, name, opts)
|
354
371
|
end
|
355
372
|
|
356
373
|
# Provides access to the default exchange
|
357
374
|
# @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
|
358
375
|
# @api public
|
359
376
|
def default_exchange
|
360
|
-
|
377
|
+
Exchange.default(self)
|
361
378
|
end
|
362
379
|
|
363
380
|
# Declares a headers exchange or looks it up in the cache of previously
|
@@ -398,27 +415,28 @@ module Bunny
|
|
398
415
|
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
|
399
416
|
# @api public
|
400
417
|
def queue(name = AMQ::Protocol::EMPTY_STRING, opts = {})
|
418
|
+
throw ArgumentError.new("queue name must not be nil") if name.nil?
|
419
|
+
|
401
420
|
q = find_queue(name) || Bunny::Queue.new(self, name, opts)
|
402
421
|
|
403
422
|
register_queue(q)
|
404
423
|
end
|
405
424
|
|
425
|
+
# Declares a new server-named queue that is automatically deleted when the
|
426
|
+
# connection is closed.
|
427
|
+
#
|
428
|
+
# @return [Bunny::Queue] Queue that was declared
|
429
|
+
# @see #queue
|
430
|
+
# @api public
|
431
|
+
def temporary_queue(opts = {})
|
432
|
+
queue("", opts.merge(:exclusive => true))
|
433
|
+
end
|
434
|
+
|
406
435
|
# @endgroup
|
407
436
|
|
408
437
|
|
409
438
|
# @group QoS and Flow Control
|
410
439
|
|
411
|
-
# Sets how many messages will be given to consumers on this channel before they
|
412
|
-
# have to acknowledge or reject one of the previously consumed messages
|
413
|
-
#
|
414
|
-
# @param [Integer] prefetch_count Prefetch (QoS setting) for this channel
|
415
|
-
# @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
|
416
|
-
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
417
|
-
# @api public
|
418
|
-
def prefetch(count)
|
419
|
-
self.basic_qos(count, false)
|
420
|
-
end
|
421
|
-
|
422
440
|
# Flow control. When set to false, RabbitMQ will stop delivering messages on this
|
423
441
|
# channel.
|
424
442
|
#
|
@@ -451,9 +469,7 @@ module Bunny
|
|
451
469
|
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
452
470
|
# @api public
|
453
471
|
def reject(delivery_tag, requeue = false)
|
454
|
-
|
455
|
-
basic_reject(delivery_tag.to_i, requeue)
|
456
|
-
end
|
472
|
+
basic_reject(delivery_tag.to_i, requeue)
|
457
473
|
end
|
458
474
|
|
459
475
|
# Acknowledges a message. Acknowledged messages are completely removed from the queue.
|
@@ -464,9 +480,7 @@ module Bunny
|
|
464
480
|
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
465
481
|
# @api public
|
466
482
|
def ack(delivery_tag, multiple = false)
|
467
|
-
|
468
|
-
basic_ack(delivery_tag.to_i, multiple)
|
469
|
-
end
|
483
|
+
basic_ack(delivery_tag.to_i, multiple)
|
470
484
|
end
|
471
485
|
alias acknowledge ack
|
472
486
|
|
@@ -481,9 +495,7 @@ module Bunny
|
|
481
495
|
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
482
496
|
# @api public
|
483
497
|
def nack(delivery_tag, multiple = false, requeue = false)
|
484
|
-
|
485
|
-
basic_nack(delivery_tag.to_i, multiple, requeue)
|
486
|
-
end
|
498
|
+
basic_nack(delivery_tag.to_i, multiple, requeue)
|
487
499
|
end
|
488
500
|
|
489
501
|
# @endgroup
|
@@ -553,7 +565,7 @@ module Bunny
|
|
553
565
|
opts[:mandatory],
|
554
566
|
false,
|
555
567
|
@connection.frame_max)
|
556
|
-
@connection.
|
568
|
+
@connection.send_frameset(frames, self)
|
557
569
|
|
558
570
|
self
|
559
571
|
end
|
@@ -565,7 +577,8 @@ module Bunny
|
|
565
577
|
# @param [String] queue Queue name
|
566
578
|
# @param [Hash] opts Options
|
567
579
|
#
|
568
|
-
# @option opts [Boolean] :ack (true)
|
580
|
+
# @option opts [Boolean] :ack (true) [DEPRECATED] Use :manual_ack instead
|
581
|
+
# @option opts [Boolean] :manual_ack (true) Will this message be acknowledged manually?
|
569
582
|
#
|
570
583
|
# @return [Array] A triple of delivery info, message properties and message content
|
571
584
|
#
|
@@ -574,15 +587,20 @@ module Bunny
|
|
574
587
|
# conn.start
|
575
588
|
# ch = conn.create_channel
|
576
589
|
# # here we assume the queue already exists and has messages
|
577
|
-
# delivery_info, properties, payload = ch.basic_get("bunny.examples.queue1", :
|
590
|
+
# delivery_info, properties, payload = ch.basic_get("bunny.examples.queue1", :manual_ack => true)
|
578
591
|
# ch.acknowledge(delivery_info.delivery_tag)
|
579
592
|
# @see Bunny::Queue#pop
|
580
593
|
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
581
594
|
# @api public
|
582
|
-
def basic_get(queue, opts = {:
|
595
|
+
def basic_get(queue, opts = {:manual_ack => true})
|
583
596
|
raise_if_no_longer_open!
|
584
597
|
|
585
|
-
|
598
|
+
unless opts[:ack].nil?
|
599
|
+
warn "[DEPRECATION] `:ack` is deprecated. Please use `:manual_ack` instead."
|
600
|
+
opts[:manual_ack] = opts[:ack]
|
601
|
+
end
|
602
|
+
|
603
|
+
@connection.send_frame(AMQ::Protocol::Basic::Get.encode(@id, queue, !(opts[:manual_ack])))
|
586
604
|
# this is a workaround for the edge case when basic_get is called in a tight loop
|
587
605
|
# and network goes down we need to perform recovery. The problem is, basic_get will
|
588
606
|
# keep blocking the thread that calls it without clear way to constantly unblock it
|
@@ -590,40 +608,59 @@ module Bunny
|
|
590
608
|
# implementation (and even more correct and convenient ones, such as wait/notify, should
|
591
609
|
# we implement them). So we return a triple of nils immediately which apps should be
|
592
610
|
# able to handle anyway as "got no message, no need to act". MK.
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
611
|
+
last_basic_get_response = if @connection.open?
|
612
|
+
begin
|
613
|
+
wait_on_basic_get_continuations
|
614
|
+
rescue Timeout::Error => e
|
615
|
+
raise_if_continuation_resulted_in_a_channel_error!
|
616
|
+
raise e
|
617
|
+
end
|
618
|
+
else
|
619
|
+
[nil, nil, nil]
|
620
|
+
end
|
598
621
|
|
599
622
|
raise_if_continuation_resulted_in_a_channel_error!
|
600
|
-
|
623
|
+
last_basic_get_response
|
601
624
|
end
|
602
625
|
|
626
|
+
# prefetch_count is of type short in the protocol. MK.
|
627
|
+
MAX_PREFETCH_COUNT = (2 ** 16) - 1
|
628
|
+
|
603
629
|
# Controls message delivery rate using basic.qos AMQP 0.9.1 method.
|
604
630
|
#
|
605
631
|
# @param [Integer] prefetch_count How many messages can consumers on this channel be given at a time
|
606
632
|
# (before they have to acknowledge or reject one of the earlier received messages)
|
607
|
-
# @param [Boolean] global
|
633
|
+
# @param [Boolean] global
|
634
|
+
# Whether to use global mode for prefetch:
|
635
|
+
# - +false+: per-consumer
|
636
|
+
# - +true+: per-channel
|
637
|
+
# Note that the default value (+false+) hasn't actually changed, but
|
638
|
+
# previous documentation described that as meaning per-channel and
|
639
|
+
# unsupported in RabbitMQ, whereas it now actually appears to mean
|
640
|
+
# per-consumer and supported
|
641
|
+
# (https://www.rabbitmq.com/consumer-prefetch.html).
|
608
642
|
# @return [AMQ::Protocol::Basic::QosOk] RabbitMQ response
|
609
643
|
# @see Bunny::Channel#prefetch
|
610
644
|
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
611
645
|
# @api public
|
612
646
|
def basic_qos(count, global = false)
|
613
|
-
raise ArgumentError.new("prefetch count must be a positive integer, given: #{
|
647
|
+
raise ArgumentError.new("prefetch count must be a positive integer, given: #{count}") if count < 0
|
648
|
+
raise ArgumentError.new("prefetch count must be no greater than #{MAX_PREFETCH_COUNT}, given: #{count}") if count > MAX_PREFETCH_COUNT
|
614
649
|
raise_if_no_longer_open!
|
615
650
|
|
616
651
|
@connection.send_frame(AMQ::Protocol::Basic::Qos.encode(@id, 0, count, global))
|
617
652
|
|
618
|
-
|
653
|
+
with_continuation_timeout do
|
619
654
|
@last_basic_qos_ok = wait_on_continuations
|
620
655
|
end
|
621
656
|
raise_if_continuation_resulted_in_a_channel_error!
|
622
657
|
|
623
|
-
@prefetch_count
|
658
|
+
@prefetch_count = count
|
659
|
+
@prefetch_global = global
|
624
660
|
|
625
661
|
@last_basic_qos_ok
|
626
662
|
end
|
663
|
+
alias prefetch basic_qos
|
627
664
|
|
628
665
|
# Redeliver unacknowledged messages
|
629
666
|
#
|
@@ -634,7 +671,7 @@ module Bunny
|
|
634
671
|
raise_if_no_longer_open!
|
635
672
|
|
636
673
|
@connection.send_frame(AMQ::Protocol::Basic::Recover.encode(@id, requeue))
|
637
|
-
|
674
|
+
with_continuation_timeout do
|
638
675
|
@last_basic_recover_ok = wait_on_continuations
|
639
676
|
end
|
640
677
|
raise_if_continuation_resulted_in_a_channel_error!
|
@@ -664,7 +701,7 @@ module Bunny
|
|
664
701
|
#
|
665
702
|
# ch = conn.create_channel
|
666
703
|
# q.subscribe do |delivery_info, properties, payload|
|
667
|
-
# #
|
704
|
+
# # reject the message
|
668
705
|
# ch.basic_reject(delivery_info.delivery_tag, false)
|
669
706
|
# end
|
670
707
|
#
|
@@ -674,17 +711,19 @@ module Bunny
|
|
674
711
|
#
|
675
712
|
# ch = conn.create_channel
|
676
713
|
# # we assume the queue exists and has messages
|
677
|
-
# delivery_info, properties, payload = ch.basic_get("bunny.examples.queue3", :
|
714
|
+
# delivery_info, properties, payload = ch.basic_get("bunny.examples.queue3", :manual_ack => true)
|
678
715
|
# ch.basic_reject(delivery_info.delivery_tag, true)
|
679
716
|
#
|
680
717
|
# @see Bunny::Channel#basic_nack
|
681
718
|
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
682
719
|
# @api public
|
683
|
-
def basic_reject(delivery_tag, requeue)
|
684
|
-
|
685
|
-
|
720
|
+
def basic_reject(delivery_tag, requeue = false)
|
721
|
+
guarding_against_stale_delivery_tags(delivery_tag) do
|
722
|
+
raise_if_no_longer_open!
|
723
|
+
@connection.send_frame(AMQ::Protocol::Basic::Reject.encode(@id, delivery_tag, requeue))
|
686
724
|
|
687
|
-
|
725
|
+
nil
|
726
|
+
end
|
688
727
|
end
|
689
728
|
|
690
729
|
# Acknowledges a delivery (message).
|
@@ -700,7 +739,7 @@ module Bunny
|
|
700
739
|
# ch = conn.create_channel
|
701
740
|
# q.subscribe do |delivery_info, properties, payload|
|
702
741
|
# # requeue the message
|
703
|
-
# ch.basic_ack(delivery_info.delivery_tag)
|
742
|
+
# ch.basic_ack(delivery_info.delivery_tag.to_i)
|
704
743
|
# end
|
705
744
|
#
|
706
745
|
# @example Ack a message fetched via basic.get
|
@@ -709,8 +748,8 @@ module Bunny
|
|
709
748
|
#
|
710
749
|
# ch = conn.create_channel
|
711
750
|
# # we assume the queue exists and has messages
|
712
|
-
# delivery_info, properties, payload = ch.basic_get("bunny.examples.queue3", :
|
713
|
-
# ch.basic_ack(delivery_info.delivery_tag)
|
751
|
+
# delivery_info, properties, payload = ch.basic_get("bunny.examples.queue3", :manual_ack => true)
|
752
|
+
# ch.basic_ack(delivery_info.delivery_tag.to_i)
|
714
753
|
#
|
715
754
|
# @example Ack multiple messages fetched via basic.get
|
716
755
|
# conn = Bunny.new
|
@@ -718,20 +757,21 @@ module Bunny
|
|
718
757
|
#
|
719
758
|
# ch = conn.create_channel
|
720
759
|
# # we assume the queue exists and has messages
|
721
|
-
# _, _, payload1 = ch.basic_get("bunny.examples.queue3", :
|
722
|
-
# _, _, payload2 = ch.basic_get("bunny.examples.queue3", :
|
723
|
-
# delivery_info, properties, payload3 = ch.basic_get("bunny.examples.queue3", :
|
760
|
+
# _, _, payload1 = ch.basic_get("bunny.examples.queue3", :manual_ack => true)
|
761
|
+
# _, _, payload2 = ch.basic_get("bunny.examples.queue3", :manual_ack => true)
|
762
|
+
# delivery_info, properties, payload3 = ch.basic_get("bunny.examples.queue3", :manual_ack => true)
|
724
763
|
# # ack all fetched messages up to payload3
|
725
|
-
# ch.basic_ack(delivery_info.delivery_tag, true)
|
764
|
+
# ch.basic_ack(delivery_info.delivery_tag.to_i, true)
|
726
765
|
#
|
727
766
|
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
728
|
-
# @see #basic_ack_known_delivery_tag
|
729
767
|
# @api public
|
730
|
-
def basic_ack(delivery_tag, multiple)
|
731
|
-
|
732
|
-
|
768
|
+
def basic_ack(delivery_tag, multiple = false)
|
769
|
+
guarding_against_stale_delivery_tags(delivery_tag) do
|
770
|
+
raise_if_no_longer_open!
|
771
|
+
@connection.send_frame(AMQ::Protocol::Basic::Ack.encode(@id, delivery_tag, multiple))
|
733
772
|
|
734
|
-
|
773
|
+
nil
|
774
|
+
end
|
735
775
|
end
|
736
776
|
|
737
777
|
# Rejects or requeues messages just like {Bunny::Channel#basic_reject} but can do so
|
@@ -768,7 +808,7 @@ module Bunny
|
|
768
808
|
#
|
769
809
|
# ch = conn.create_channel
|
770
810
|
# # we assume the queue exists and has messages
|
771
|
-
# delivery_info, properties, payload = ch.basic_get("bunny.examples.queue3", :
|
811
|
+
# delivery_info, properties, payload = ch.basic_get("bunny.examples.queue3", :manual_ack => true)
|
772
812
|
# ch.basic_nack(delivery_info.delivery_tag, false, true)
|
773
813
|
#
|
774
814
|
#
|
@@ -778,9 +818,9 @@ module Bunny
|
|
778
818
|
#
|
779
819
|
# ch = conn.create_channel
|
780
820
|
# # we assume the queue exists and has messages
|
781
|
-
# _, _, payload1 = ch.basic_get("bunny.examples.queue3", :
|
782
|
-
# _, _, payload2 = ch.basic_get("bunny.examples.queue3", :
|
783
|
-
# delivery_info, properties, payload3 = ch.basic_get("bunny.examples.queue3", :
|
821
|
+
# _, _, payload1 = ch.basic_get("bunny.examples.queue3", :manual_ack => true)
|
822
|
+
# _, _, payload2 = ch.basic_get("bunny.examples.queue3", :manual_ack => true)
|
823
|
+
# delivery_info, properties, payload3 = ch.basic_get("bunny.examples.queue3", :manual_ack => true)
|
784
824
|
# # requeue all fetched messages up to payload3
|
785
825
|
# ch.basic_nack(delivery_info.delivery_tag, true, true)
|
786
826
|
#
|
@@ -788,13 +828,15 @@ module Bunny
|
|
788
828
|
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
|
789
829
|
# @api public
|
790
830
|
def basic_nack(delivery_tag, multiple = false, requeue = false)
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
831
|
+
guarding_against_stale_delivery_tags(delivery_tag) do
|
832
|
+
raise_if_no_longer_open!
|
833
|
+
@connection.send_frame(AMQ::Protocol::Basic::Nack.encode(@id,
|
834
|
+
delivery_tag,
|
835
|
+
multiple,
|
836
|
+
requeue))
|
796
837
|
|
797
|
-
|
838
|
+
nil
|
839
|
+
end
|
798
840
|
end
|
799
841
|
|
800
842
|
# Registers a consumer for queue. Delivered messages will be handled with the block
|
@@ -836,7 +878,7 @@ module Bunny
|
|
836
878
|
arguments))
|
837
879
|
|
838
880
|
begin
|
839
|
-
|
881
|
+
with_continuation_timeout do
|
840
882
|
@last_basic_consume_ok = wait_on_continuations
|
841
883
|
end
|
842
884
|
rescue Exception => e
|
@@ -886,7 +928,7 @@ module Bunny
|
|
886
928
|
consumer.arguments))
|
887
929
|
|
888
930
|
begin
|
889
|
-
|
931
|
+
with_continuation_timeout do
|
890
932
|
@last_basic_consume_ok = wait_on_continuations
|
891
933
|
end
|
892
934
|
rescue Exception => e
|
@@ -921,11 +963,13 @@ module Bunny
|
|
921
963
|
def basic_cancel(consumer_tag)
|
922
964
|
@connection.send_frame(AMQ::Protocol::Basic::Cancel.encode(@id, consumer_tag, false))
|
923
965
|
|
924
|
-
|
966
|
+
with_continuation_timeout do
|
925
967
|
@last_basic_cancel_ok = wait_on_continuations
|
926
968
|
end
|
927
969
|
|
928
|
-
|
970
|
+
# reduces thread usage for channels that don't have any
|
971
|
+
# consumers
|
972
|
+
@work_pool.shutdown(true) unless self.any_consumers?
|
929
973
|
|
930
974
|
@last_basic_cancel_ok
|
931
975
|
end
|
@@ -943,7 +987,8 @@ module Bunny
|
|
943
987
|
|
944
988
|
# Declares a queue using queue.declare AMQP 0.9.1 method.
|
945
989
|
#
|
946
|
-
# @param [String] name
|
990
|
+
# @param [String] name The name of the queue or an empty string to let RabbitMQ generate a name.
|
991
|
+
# Note that LF and CR characters will be stripped from the value.
|
947
992
|
# @param [Hash] opts Queue properties
|
948
993
|
#
|
949
994
|
# @option opts [Boolean] durable (false) Should information about this queue be persisted to disk so that it
|
@@ -961,16 +1006,28 @@ module Bunny
|
|
961
1006
|
def queue_declare(name, opts = {})
|
962
1007
|
raise_if_no_longer_open!
|
963
1008
|
|
964
|
-
|
965
|
-
|
1009
|
+
# strip trailing new line and carriage returns
|
1010
|
+
# just like RabbitMQ does
|
1011
|
+
safe_name = name.gsub(/[\r\n]/, "")
|
1012
|
+
@pending_queue_declare_name = safe_name
|
1013
|
+
@connection.send_frame(
|
1014
|
+
AMQ::Protocol::Queue::Declare.encode(@id,
|
1015
|
+
@pending_queue_declare_name,
|
966
1016
|
opts.fetch(:passive, false),
|
967
1017
|
opts.fetch(:durable, false),
|
968
1018
|
opts.fetch(:exclusive, false),
|
969
1019
|
opts.fetch(:auto_delete, false),
|
970
1020
|
false,
|
971
1021
|
opts[:arguments]))
|
972
|
-
@last_queue_declare_ok = wait_on_continuations
|
973
1022
|
|
1023
|
+
begin
|
1024
|
+
with_continuation_timeout do
|
1025
|
+
@last_queue_declare_ok = wait_on_continuations
|
1026
|
+
end
|
1027
|
+
ensure
|
1028
|
+
# clear pending continuation context if it belongs to us
|
1029
|
+
@pending_queue_declare_name = nil if @pending_queue_declare_name == safe_name
|
1030
|
+
end
|
974
1031
|
raise_if_continuation_resulted_in_a_channel_error!
|
975
1032
|
|
976
1033
|
@last_queue_declare_ok
|
@@ -995,7 +1052,7 @@ module Bunny
|
|
995
1052
|
opts[:if_unused],
|
996
1053
|
opts[:if_empty],
|
997
1054
|
false))
|
998
|
-
|
1055
|
+
with_continuation_timeout do
|
999
1056
|
@last_queue_delete_ok = wait_on_continuations
|
1000
1057
|
end
|
1001
1058
|
raise_if_continuation_resulted_in_a_channel_error!
|
@@ -1015,7 +1072,7 @@ module Bunny
|
|
1015
1072
|
|
1016
1073
|
@connection.send_frame(AMQ::Protocol::Queue::Purge.encode(@id, name, false))
|
1017
1074
|
|
1018
|
-
|
1075
|
+
with_continuation_timeout do
|
1019
1076
|
@last_queue_purge_ok = wait_on_continuations
|
1020
1077
|
end
|
1021
1078
|
raise_if_continuation_resulted_in_a_channel_error!
|
@@ -1051,7 +1108,7 @@ module Bunny
|
|
1051
1108
|
(opts[:routing_key] || opts[:key]),
|
1052
1109
|
false,
|
1053
1110
|
opts[:arguments]))
|
1054
|
-
|
1111
|
+
with_continuation_timeout do
|
1055
1112
|
@last_queue_bind_ok = wait_on_continuations
|
1056
1113
|
end
|
1057
1114
|
|
@@ -1086,7 +1143,7 @@ module Bunny
|
|
1086
1143
|
exchange_name,
|
1087
1144
|
opts[:routing_key],
|
1088
1145
|
opts[:arguments]))
|
1089
|
-
|
1146
|
+
with_continuation_timeout do
|
1090
1147
|
@last_queue_unbind_ok = wait_on_continuations
|
1091
1148
|
end
|
1092
1149
|
|
@@ -1099,25 +1156,30 @@ module Bunny
|
|
1099
1156
|
|
1100
1157
|
# @group Exchange operations (exchange.*)
|
1101
1158
|
|
1102
|
-
# Declares a
|
1159
|
+
# Declares a exchange using exchange.declare AMQP 0.9.1 method.
|
1103
1160
|
#
|
1104
|
-
# @param [String] name
|
1161
|
+
# @param [String] name The name of the exchange. Note that LF and CR characters
|
1162
|
+
# will be stripped from the value.
|
1163
|
+
# @param [String,Symbol] type Exchange type, e.g. :fanout or :topic
|
1105
1164
|
# @param [Hash] opts Exchange properties
|
1106
1165
|
#
|
1107
|
-
# @option opts [Boolean] durable (false) Should information about this
|
1166
|
+
# @option opts [Boolean] durable (false) Should information about this exchange be persisted to disk so that it
|
1108
1167
|
# can survive broker restarts? Typically set to true for long-lived exchanges.
|
1109
|
-
# @option opts [Boolean] auto_delete (false) Should this
|
1168
|
+
# @option opts [Boolean] auto_delete (false) Should this exchange be deleted when it is no longer used?
|
1110
1169
|
# @option opts [Boolean] passive (false) If true, exchange will be checked for existence. If it does not
|
1111
1170
|
# exist, {Bunny::NotFound} will be raised.
|
1112
1171
|
#
|
1113
1172
|
# @return [AMQ::Protocol::Exchange::DeclareOk] RabbitMQ response
|
1114
|
-
# @see http://rubybunny.info/articles/
|
1173
|
+
# @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
|
1115
1174
|
# @api public
|
1116
1175
|
def exchange_declare(name, type, opts = {})
|
1117
1176
|
raise_if_no_longer_open!
|
1118
1177
|
|
1178
|
+
# strip trailing new line and carriage returns
|
1179
|
+
# just like RabbitMQ does
|
1180
|
+
safe_name = name.gsub(/[\r\n]/, "")
|
1119
1181
|
@connection.send_frame(AMQ::Protocol::Exchange::Declare.encode(@id,
|
1120
|
-
|
1182
|
+
safe_name,
|
1121
1183
|
type.to_s,
|
1122
1184
|
opts.fetch(:passive, false),
|
1123
1185
|
opts.fetch(:durable, false),
|
@@ -1125,7 +1187,7 @@ module Bunny
|
|
1125
1187
|
opts.fetch(:internal, false),
|
1126
1188
|
false, # nowait
|
1127
1189
|
opts[:arguments]))
|
1128
|
-
|
1190
|
+
with_continuation_timeout do
|
1129
1191
|
@last_exchange_declare_ok = wait_on_continuations
|
1130
1192
|
end
|
1131
1193
|
|
@@ -1150,7 +1212,7 @@ module Bunny
|
|
1150
1212
|
name,
|
1151
1213
|
opts[:if_unused],
|
1152
1214
|
false))
|
1153
|
-
|
1215
|
+
with_continuation_timeout do
|
1154
1216
|
@last_exchange_delete_ok = wait_on_continuations
|
1155
1217
|
end
|
1156
1218
|
|
@@ -1194,7 +1256,7 @@ module Bunny
|
|
1194
1256
|
opts[:routing_key],
|
1195
1257
|
false,
|
1196
1258
|
opts[:arguments]))
|
1197
|
-
|
1259
|
+
with_continuation_timeout do
|
1198
1260
|
@last_exchange_bind_ok = wait_on_continuations
|
1199
1261
|
end
|
1200
1262
|
|
@@ -1238,7 +1300,7 @@ module Bunny
|
|
1238
1300
|
opts[:routing_key],
|
1239
1301
|
false,
|
1240
1302
|
opts[:arguments]))
|
1241
|
-
|
1303
|
+
with_continuation_timeout do
|
1242
1304
|
@last_exchange_unbind_ok = wait_on_continuations
|
1243
1305
|
end
|
1244
1306
|
|
@@ -1266,7 +1328,7 @@ module Bunny
|
|
1266
1328
|
raise_if_no_longer_open!
|
1267
1329
|
|
1268
1330
|
@connection.send_frame(AMQ::Protocol::Channel::Flow.encode(@id, active))
|
1269
|
-
|
1331
|
+
with_continuation_timeout do
|
1270
1332
|
@last_channel_flow_ok = wait_on_continuations
|
1271
1333
|
end
|
1272
1334
|
raise_if_continuation_resulted_in_a_channel_error!
|
@@ -1287,7 +1349,7 @@ module Bunny
|
|
1287
1349
|
raise_if_no_longer_open!
|
1288
1350
|
|
1289
1351
|
@connection.send_frame(AMQ::Protocol::Tx::Select.encode(@id))
|
1290
|
-
|
1352
|
+
with_continuation_timeout do
|
1291
1353
|
@last_tx_select_ok = wait_on_continuations
|
1292
1354
|
end
|
1293
1355
|
raise_if_continuation_resulted_in_a_channel_error!
|
@@ -1303,7 +1365,7 @@ module Bunny
|
|
1303
1365
|
raise_if_no_longer_open!
|
1304
1366
|
|
1305
1367
|
@connection.send_frame(AMQ::Protocol::Tx::Commit.encode(@id))
|
1306
|
-
|
1368
|
+
with_continuation_timeout do
|
1307
1369
|
@last_tx_commit_ok = wait_on_continuations
|
1308
1370
|
end
|
1309
1371
|
raise_if_continuation_resulted_in_a_channel_error!
|
@@ -1318,7 +1380,7 @@ module Bunny
|
|
1318
1380
|
raise_if_no_longer_open!
|
1319
1381
|
|
1320
1382
|
@connection.send_frame(AMQ::Protocol::Tx::Rollback.encode(@id))
|
1321
|
-
|
1383
|
+
with_continuation_timeout do
|
1322
1384
|
@last_tx_rollback_ok = wait_on_continuations
|
1323
1385
|
end
|
1324
1386
|
raise_if_continuation_resulted_in_a_channel_error!
|
@@ -1351,7 +1413,7 @@ module Bunny
|
|
1351
1413
|
# @see #nacked_set
|
1352
1414
|
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
|
1353
1415
|
# @api public
|
1354
|
-
def confirm_select(callback=nil)
|
1416
|
+
def confirm_select(callback = nil)
|
1355
1417
|
raise_if_no_longer_open!
|
1356
1418
|
|
1357
1419
|
if @next_publish_seq_no == 0
|
@@ -1359,33 +1421,32 @@ module Bunny
|
|
1359
1421
|
@unconfirmed_set = Set.new
|
1360
1422
|
@nacked_set = Set.new
|
1361
1423
|
@next_publish_seq_no = 1
|
1424
|
+
@only_acks_received = true
|
1362
1425
|
end
|
1363
1426
|
|
1364
1427
|
@confirms_callback = callback
|
1365
1428
|
|
1366
1429
|
@connection.send_frame(AMQ::Protocol::Confirm::Select.encode(@id, false))
|
1367
|
-
|
1430
|
+
with_continuation_timeout do
|
1368
1431
|
@last_confirm_select_ok = wait_on_continuations
|
1369
1432
|
end
|
1370
|
-
@confirm_mode = true
|
1371
1433
|
raise_if_continuation_resulted_in_a_channel_error!
|
1372
1434
|
@last_confirm_select_ok
|
1373
1435
|
end
|
1374
1436
|
|
1375
1437
|
# Blocks calling thread until confirms are received for all
|
1376
|
-
# currently unacknowledged published messages.
|
1438
|
+
# currently unacknowledged published messages. Returns immediately
|
1439
|
+
# if there are no outstanding confirms.
|
1377
1440
|
#
|
1378
|
-
# @return [Boolean] true if all messages were acknowledged positively, false otherwise
|
1441
|
+
# @return [Boolean] true if all messages were acknowledged positively since the last time this method was called, false otherwise
|
1379
1442
|
# @see #confirm_select
|
1380
1443
|
# @see #unconfirmed_set
|
1381
1444
|
# @see #nacked_set
|
1382
1445
|
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
|
1383
1446
|
# @api public
|
1384
1447
|
def wait_for_confirms
|
1385
|
-
@only_acks_received = true
|
1386
1448
|
wait_on_confirms_continuations
|
1387
|
-
|
1388
|
-
@only_acks_received
|
1449
|
+
read_and_reset_only_acks_received
|
1389
1450
|
end
|
1390
1451
|
|
1391
1452
|
# @endgroup
|
@@ -1441,7 +1502,7 @@ module Bunny
|
|
1441
1502
|
#
|
1442
1503
|
# @api plugin
|
1443
1504
|
def recover_from_network_failure
|
1444
|
-
@logger.debug "Recovering channel #{@id} after network failure"
|
1505
|
+
@logger.debug { "Recovering channel #{@id} after network failure" }
|
1445
1506
|
release_all_continuations
|
1446
1507
|
|
1447
1508
|
recover_prefetch_setting
|
@@ -1459,7 +1520,7 @@ module Bunny
|
|
1459
1520
|
#
|
1460
1521
|
# @api plugin
|
1461
1522
|
def recover_prefetch_setting
|
1462
|
-
basic_qos(@prefetch_count) if @prefetch_count
|
1523
|
+
basic_qos(@prefetch_count, @prefetch_global) if @prefetch_count
|
1463
1524
|
end
|
1464
1525
|
|
1465
1526
|
# Recovers publisher confirms mode. Used by the Automatic Network Failure
|
@@ -1467,7 +1528,11 @@ module Bunny
|
|
1467
1528
|
#
|
1468
1529
|
# @api plugin
|
1469
1530
|
def recover_confirm_mode
|
1470
|
-
|
1531
|
+
if using_publisher_confirmations?
|
1532
|
+
@unconfirmed_set.clear
|
1533
|
+
@delivery_tag_offset = @next_publish_seq_no - 1
|
1534
|
+
confirm_select(@confirms_callback)
|
1535
|
+
end
|
1471
1536
|
end
|
1472
1537
|
|
1473
1538
|
# Recovers transaction mode. Used by the Automatic Network Failure
|
@@ -1483,7 +1548,7 @@ module Bunny
|
|
1483
1548
|
#
|
1484
1549
|
# @api plugin
|
1485
1550
|
def recover_exchanges
|
1486
|
-
@exchanges.values.
|
1551
|
+
@exchange_mutex.synchronize { @exchanges.values }.each do |x|
|
1487
1552
|
x.recover_from_network_failure
|
1488
1553
|
end
|
1489
1554
|
end
|
@@ -1493,8 +1558,8 @@ module Bunny
|
|
1493
1558
|
#
|
1494
1559
|
# @api plugin
|
1495
1560
|
def recover_queues
|
1496
|
-
@queues.values.
|
1497
|
-
@logger.debug "Recovering queue #{q.name}"
|
1561
|
+
@queue_mutex.synchronize { @queues.values }.each do |q|
|
1562
|
+
@logger.debug { "Recovering queue #{q.name}" }
|
1498
1563
|
q.recover_from_network_failure
|
1499
1564
|
end
|
1500
1565
|
end
|
@@ -1505,10 +1570,11 @@ module Bunny
|
|
1505
1570
|
# @api plugin
|
1506
1571
|
def recover_consumers
|
1507
1572
|
unless @consumers.empty?
|
1508
|
-
@work_pool = ConsumerWorkPool.new(@work_pool.size)
|
1573
|
+
@work_pool = ConsumerWorkPool.new(@work_pool.size, @work_pool.abort_on_exception)
|
1509
1574
|
@work_pool.start
|
1510
1575
|
end
|
1511
|
-
|
1576
|
+
|
1577
|
+
@consumer_mutex.synchronize { @consumers.values }.each do |c|
|
1512
1578
|
c.recover_from_network_failure
|
1513
1579
|
end
|
1514
1580
|
end
|
@@ -1533,7 +1599,11 @@ module Bunny
|
|
1533
1599
|
|
1534
1600
|
# @return [String] Brief human-readable representation of the channel
|
1535
1601
|
def to_s
|
1536
|
-
"#<#{self.class.name}:#{object_id} @id=#{self.number} @connection=#{@connection.to_s}>"
|
1602
|
+
"#<#{self.class.name}:#{object_id} @id=#{self.number} @connection=#{@connection.to_s}> @open=#{open?}"
|
1603
|
+
end
|
1604
|
+
|
1605
|
+
def inspect
|
1606
|
+
to_s
|
1537
1607
|
end
|
1538
1608
|
|
1539
1609
|
|
@@ -1541,6 +1611,11 @@ module Bunny
|
|
1541
1611
|
# Implementation
|
1542
1612
|
#
|
1543
1613
|
|
1614
|
+
# @private
|
1615
|
+
def with_continuation_timeout(&block)
|
1616
|
+
Bunny::Timeout.timeout(wait_on_continuations_timeout, ClientTimeout, &block)
|
1617
|
+
end
|
1618
|
+
|
1544
1619
|
# @private
|
1545
1620
|
def register_consumer(consumer_tag, consumer)
|
1546
1621
|
@consumer_mutex.synchronize do
|
@@ -1564,12 +1639,33 @@ module Bunny
|
|
1564
1639
|
end
|
1565
1640
|
end
|
1566
1641
|
|
1642
|
+
# @private
|
1643
|
+
def pending_server_named_queue_declaration?
|
1644
|
+
@pending_queue_declare_name && @pending_queue_declare_name.empty?
|
1645
|
+
end
|
1646
|
+
|
1647
|
+
# @private
|
1648
|
+
def can_accept_queue_declare_ok?(method)
|
1649
|
+
@pending_queue_declare_name == method.queue ||
|
1650
|
+
pending_server_named_queue_declaration?
|
1651
|
+
end
|
1652
|
+
|
1567
1653
|
# @private
|
1568
1654
|
def handle_method(method)
|
1569
|
-
@logger.debug "Channel#handle_frame on channel #{@id}: #{method.inspect}"
|
1655
|
+
@logger.debug { "Channel#handle_frame on channel #{@id}: #{method.inspect}" }
|
1570
1656
|
case method
|
1571
1657
|
when AMQ::Protocol::Queue::DeclareOk then
|
1572
|
-
|
1658
|
+
# safeguard against late arrivals of responses and
|
1659
|
+
# so on, see ruby-amqp/bunny#558
|
1660
|
+
if can_accept_queue_declare_ok?(method)
|
1661
|
+
@continuations.push(method)
|
1662
|
+
else
|
1663
|
+
if !pending_server_named_queue_declaration?
|
1664
|
+
# this response is for an outdated/overwritten
|
1665
|
+
# queue.declare, drop it
|
1666
|
+
@logger.warn "Received a queue.declare-ok response for a mismatching queue (#{method.queue} instead of #{@pending_queue_declare_name}) on channel #{@id} possibly due to a timeout, ignoring it"
|
1667
|
+
end
|
1668
|
+
end
|
1573
1669
|
when AMQ::Protocol::Queue::DeleteOk then
|
1574
1670
|
@continuations.push(method)
|
1575
1671
|
when AMQ::Protocol::Queue::PurgeOk then
|
@@ -1691,28 +1787,26 @@ module Bunny
|
|
1691
1787
|
end
|
1692
1788
|
|
1693
1789
|
# @private
|
1694
|
-
def handle_ack_or_nack(
|
1790
|
+
def handle_ack_or_nack(delivery_tag_before_offset, multiple, nack)
|
1791
|
+
delivery_tag = delivery_tag_before_offset + @delivery_tag_offset
|
1792
|
+
confirmed_range_start = multiple ? @delivery_tag_offset + 1 : delivery_tag
|
1793
|
+
confirmed_range_end = delivery_tag
|
1794
|
+
confirmed_range = (confirmed_range_start..confirmed_range_end)
|
1795
|
+
|
1695
1796
|
@unconfirmed_set_mutex.synchronize do
|
1696
1797
|
if nack
|
1697
|
-
|
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
|
1798
|
+
@nacked_set.merge(@unconfirmed_set & confirmed_range)
|
1704
1799
|
end
|
1705
1800
|
|
1706
|
-
|
1707
|
-
@unconfirmed_set.delete_if { |i| i <= delivery_tag }
|
1708
|
-
else
|
1709
|
-
@unconfirmed_set.delete(delivery_tag)
|
1710
|
-
end
|
1801
|
+
@unconfirmed_set.subtract(confirmed_range)
|
1711
1802
|
|
1712
1803
|
@only_acks_received = (@only_acks_received && !nack)
|
1713
1804
|
|
1714
1805
|
@confirms_continuations.push(true) if @unconfirmed_set.empty?
|
1715
|
-
|
1806
|
+
|
1807
|
+
if @confirms_callback
|
1808
|
+
confirmed_range.each { |tag| @confirms_callback.call(tag, false, nack) }
|
1809
|
+
end
|
1716
1810
|
end
|
1717
1811
|
end
|
1718
1812
|
|
@@ -1754,22 +1848,37 @@ module Bunny
|
|
1754
1848
|
|
1755
1849
|
# @private
|
1756
1850
|
def wait_on_confirms_continuations
|
1851
|
+
raise_if_no_longer_open!
|
1852
|
+
|
1757
1853
|
if @connection.threaded
|
1758
1854
|
t = Thread.current
|
1759
1855
|
@threads_waiting_on_confirms_continuations << t
|
1760
1856
|
|
1761
1857
|
begin
|
1762
|
-
@
|
1858
|
+
while @unconfirmed_set_mutex.synchronize { !@unconfirmed_set.empty? }
|
1859
|
+
@confirms_continuations.poll(@connection.continuation_timeout)
|
1860
|
+
end
|
1763
1861
|
ensure
|
1764
1862
|
@threads_waiting_on_confirms_continuations.delete(t)
|
1765
1863
|
end
|
1766
1864
|
else
|
1767
|
-
|
1865
|
+
unless @unconfirmed_set.empty?
|
1866
|
+
connection.reader_loop.run_once until @confirms_continuations.length > 0
|
1867
|
+
@confirms_continuations.pop
|
1868
|
+
end
|
1869
|
+
end
|
1870
|
+
end
|
1768
1871
|
|
1769
|
-
|
1872
|
+
# @private
|
1873
|
+
def read_and_reset_only_acks_received
|
1874
|
+
@unconfirmed_set_mutex.synchronize do
|
1875
|
+
result = @only_acks_received
|
1876
|
+
@only_acks_received = true
|
1877
|
+
result
|
1770
1878
|
end
|
1771
1879
|
end
|
1772
1880
|
|
1881
|
+
|
1773
1882
|
# Releases all continuations. Used by automatic network recovery.
|
1774
1883
|
# @private
|
1775
1884
|
def release_all_continuations
|
@@ -1815,37 +1924,37 @@ module Bunny
|
|
1815
1924
|
|
1816
1925
|
# @private
|
1817
1926
|
def deregister_queue(queue)
|
1818
|
-
@queues.delete(queue.name)
|
1927
|
+
@queue_mutex.synchronize { @queues.delete(queue.name) }
|
1819
1928
|
end
|
1820
1929
|
|
1821
1930
|
# @private
|
1822
1931
|
def deregister_queue_named(name)
|
1823
|
-
@queues.delete(name)
|
1932
|
+
@queue_mutex.synchronize { @queues.delete(name) }
|
1824
1933
|
end
|
1825
1934
|
|
1826
1935
|
# @private
|
1827
1936
|
def register_queue(queue)
|
1828
|
-
@queues[queue.name] = queue
|
1937
|
+
@queue_mutex.synchronize { @queues[queue.name] = queue }
|
1829
1938
|
end
|
1830
1939
|
|
1831
1940
|
# @private
|
1832
1941
|
def find_queue(name)
|
1833
|
-
@queues[name]
|
1942
|
+
@queue_mutex.synchronize { @queues[name] }
|
1834
1943
|
end
|
1835
1944
|
|
1836
1945
|
# @private
|
1837
1946
|
def deregister_exchange(exchange)
|
1838
|
-
@exchanges.delete(exchange.name)
|
1947
|
+
@exchange_mutex.synchronize { @exchanges.delete(exchange.name) }
|
1839
1948
|
end
|
1840
1949
|
|
1841
1950
|
# @private
|
1842
1951
|
def register_exchange(exchange)
|
1843
|
-
@exchanges[exchange.name] = exchange
|
1952
|
+
@exchange_mutex.synchronize { @exchanges[exchange.name] = exchange }
|
1844
1953
|
end
|
1845
1954
|
|
1846
1955
|
# @private
|
1847
1956
|
def find_exchange(name)
|
1848
|
-
@exchanges[name]
|
1957
|
+
@exchange_mutex.synchronize { @exchanges[name] }
|
1849
1958
|
end
|
1850
1959
|
|
1851
1960
|
protected
|
@@ -1885,7 +1994,13 @@ module Bunny
|
|
1885
1994
|
|
1886
1995
|
# @private
|
1887
1996
|
def raise_if_no_longer_open!
|
1888
|
-
|
1997
|
+
if closed?
|
1998
|
+
if @last_channel_error
|
1999
|
+
raise ChannelAlreadyClosed.new("cannot use a closed channel! Channel id: #{@id}, closed due to a server-reported channel error: #{@last_channel_error.message}", self)
|
2000
|
+
else
|
2001
|
+
raise ChannelAlreadyClosed.new("cannot use a closed channel! Channel id: #{@id}", self)
|
2002
|
+
end
|
2003
|
+
end
|
1889
2004
|
end
|
1890
2005
|
|
1891
2006
|
# @private
|
@@ -1925,7 +2040,7 @@ module Bunny
|
|
1925
2040
|
def guarding_against_stale_delivery_tags(tag, &block)
|
1926
2041
|
case tag
|
1927
2042
|
# if a fixnum was passed, execute unconditionally. MK.
|
1928
|
-
when
|
2043
|
+
when Integer then
|
1929
2044
|
block.call
|
1930
2045
|
# versioned delivery tags should be checked to avoid
|
1931
2046
|
# sending out stale (invalid) tags after channel was reopened
|