bunny 1.0.7 → 2.24.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/README.md +92 -87
- data/lib/amq/protocol/extensions.rb +2 -0
- data/lib/bunny/authentication/credentials_encoder.rb +2 -0
- data/lib/bunny/authentication/external_mechanism_encoder.rb +2 -0
- data/lib/bunny/authentication/plain_mechanism_encoder.rb +2 -0
- data/lib/bunny/channel.rb +485 -186
- data/lib/bunny/channel_id_allocator.rb +8 -4
- data/lib/bunny/concurrent/atomic_fixnum.rb +2 -0
- data/lib/bunny/concurrent/condition.rb +2 -0
- data/lib/bunny/concurrent/continuation_queue.rb +37 -13
- data/lib/bunny/concurrent/synchronized_sorted_set.rb +2 -0
- data/lib/bunny/consumer.rb +20 -13
- data/lib/bunny/consumer_tag_generator.rb +6 -2
- data/lib/bunny/consumer_work_pool.rb +37 -7
- data/lib/bunny/cruby/socket.rb +51 -22
- data/lib/bunny/cruby/ssl_socket.rb +68 -5
- data/lib/bunny/delivery_info.rb +3 -1
- data/lib/bunny/exceptions.rb +27 -4
- data/lib/bunny/exchange.rb +35 -29
- data/lib/bunny/framing.rb +2 -0
- data/lib/bunny/get_response.rb +85 -0
- data/lib/bunny/heartbeat_sender.rb +9 -6
- data/lib/bunny/message_properties.rb +2 -0
- data/lib/bunny/queue.rb +89 -41
- data/lib/bunny/reader_loop.rb +72 -28
- data/lib/bunny/return_info.rb +2 -0
- data/lib/bunny/session.rb +621 -225
- data/lib/bunny/socket.rb +7 -12
- data/lib/bunny/ssl_socket.rb +7 -12
- data/lib/bunny/test_kit.rb +15 -0
- data/lib/bunny/timeout.rb +3 -12
- data/lib/bunny/timestamp.rb +24 -0
- data/lib/bunny/transport.rb +223 -98
- data/lib/bunny/version.rb +2 -1
- data/lib/bunny/versioned_delivery_tag.rb +2 -0
- data/lib/bunny.rb +54 -8
- metadata +38 -224
- data/.gitignore +0 -22
- data/.rspec +0 -3
- data/.ruby-version +0 -1
- data/.travis.yml +0 -23
- data/.yardopts +0 -8
- data/ChangeLog.md +0 -1092
- data/Gemfile +0 -54
- data/LICENSE +0 -21
- data/benchmarks/basic_publish/with_128K_messages.rb +0 -35
- data/benchmarks/basic_publish/with_1k_messages.rb +0 -35
- data/benchmarks/basic_publish/with_4K_messages.rb +0 -35
- data/benchmarks/basic_publish/with_64K_messages.rb +0 -35
- data/benchmarks/channel_open.rb +0 -28
- data/benchmarks/mutex_and_monitor.rb +0 -42
- data/benchmarks/queue_declare.rb +0 -29
- data/benchmarks/queue_declare_and_bind.rb +0 -29
- data/benchmarks/queue_declare_bind_and_delete.rb +0 -29
- data/benchmarks/synchronized_sorted_set.rb +0 -53
- data/benchmarks/write_vs_write_nonblock.rb +0 -49
- data/bin/ci/before_build.sh +0 -31
- data/bunny.gemspec +0 -40
- data/examples/connection/authentication_failure.rb +0 -16
- data/examples/connection/automatic_recovery_with_basic_get.rb +0 -40
- data/examples/connection/automatic_recovery_with_client_named_queues.rb +0 -36
- data/examples/connection/automatic_recovery_with_multiple_consumers.rb +0 -46
- data/examples/connection/automatic_recovery_with_server_named_queues.rb +0 -35
- data/examples/connection/channel_level_exception.rb +0 -35
- data/examples/connection/disabled_automatic_recovery.rb +0 -34
- data/examples/connection/heartbeat.rb +0 -17
- data/examples/connection/manually_reconnecting_consumer.rb +0 -23
- data/examples/connection/manually_reconnecting_publisher.rb +0 -28
- data/examples/connection/unknown_host.rb +0 -16
- data/examples/guides/exchanges/direct_exchange_routing.rb +0 -36
- data/examples/guides/exchanges/fanout_exchange_routing.rb +0 -28
- data/examples/guides/exchanges/headers_exchange_routing.rb +0 -31
- data/examples/guides/exchanges/mandatory_messages.rb +0 -30
- data/examples/guides/extensions/alternate_exchange.rb +0 -28
- data/examples/guides/extensions/basic_nack.rb +0 -33
- data/examples/guides/extensions/connection_blocked.rb +0 -35
- data/examples/guides/extensions/consumer_cancellation_notification.rb +0 -39
- data/examples/guides/extensions/dead_letter_exchange.rb +0 -32
- data/examples/guides/extensions/exchange_to_exchange_bindings.rb +0 -29
- data/examples/guides/extensions/per_message_ttl.rb +0 -36
- data/examples/guides/extensions/per_queue_message_ttl.rb +0 -36
- data/examples/guides/extensions/publisher_confirms.rb +0 -28
- data/examples/guides/extensions/queue_lease.rb +0 -26
- data/examples/guides/extensions/sender_selected_distribution.rb +0 -32
- data/examples/guides/getting_started/blabbr.rb +0 -27
- data/examples/guides/getting_started/hello_world.rb +0 -20
- data/examples/guides/getting_started/weathr.rb +0 -47
- data/examples/guides/queues/one_off_consumer.rb +0 -23
- data/examples/guides/queues/redeliveries.rb +0 -79
- data/lib/bunny/compatibility.rb +0 -24
- data/lib/bunny/concurrent/linked_continuation_queue.rb +0 -61
- data/lib/bunny/jruby/socket.rb +0 -40
- data/lib/bunny/jruby/ssl_socket.rb +0 -53
- data/lib/bunny/system_timer.rb +0 -20
- data/profiling/basic_publish/with_4K_messages.rb +0 -33
- data/repl +0 -3
- 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_ack_spec.rb +0 -71
- data/spec/higher_level_api/integration/basic_cancel_spec.rb +0 -76
- data/spec/higher_level_api/integration/basic_consume_spec.rb +0 -225
- data/spec/higher_level_api/integration/basic_consume_with_objects_spec.rb +0 -54
- data/spec/higher_level_api/integration/basic_get_spec.rb +0 -48
- data/spec/higher_level_api/integration/basic_nack_spec.rb +0 -79
- data/spec/higher_level_api/integration/basic_publish_spec.rb +0 -89
- data/spec/higher_level_api/integration/basic_qos_spec.rb +0 -29
- data/spec/higher_level_api/integration/basic_recover_spec.rb +0 -18
- data/spec/higher_level_api/integration/basic_reject_spec.rb +0 -74
- data/spec/higher_level_api/integration/basic_return_spec.rb +0 -33
- data/spec/higher_level_api/integration/channel_close_spec.rb +0 -25
- data/spec/higher_level_api/integration/channel_flow_spec.rb +0 -21
- data/spec/higher_level_api/integration/channel_open_spec.rb +0 -57
- data/spec/higher_level_api/integration/confirm_select_spec.rb +0 -19
- data/spec/higher_level_api/integration/connection_spec.rb +0 -400
- data/spec/higher_level_api/integration/connection_stop_spec.rb +0 -26
- data/spec/higher_level_api/integration/consistent_hash_exchange_spec.rb +0 -50
- data/spec/higher_level_api/integration/consumer_cancellation_notification_spec.rb +0 -128
- data/spec/higher_level_api/integration/dead_lettering_spec.rb +0 -52
- data/spec/higher_level_api/integration/exchange_bind_spec.rb +0 -31
- data/spec/higher_level_api/integration/exchange_declare_spec.rb +0 -204
- data/spec/higher_level_api/integration/exchange_delete_spec.rb +0 -105
- data/spec/higher_level_api/integration/exchange_unbind_spec.rb +0 -40
- data/spec/higher_level_api/integration/exclusive_queue_spec.rb +0 -28
- data/spec/higher_level_api/integration/heartbeat_spec.rb +0 -31
- data/spec/higher_level_api/integration/merry_go_round_spec.rb +0 -85
- data/spec/higher_level_api/integration/message_properties_access_spec.rb +0 -95
- data/spec/higher_level_api/integration/predeclared_exchanges_spec.rb +0 -24
- data/spec/higher_level_api/integration/publisher_confirms_spec.rb +0 -77
- data/spec/higher_level_api/integration/publishing_edge_cases_spec.rb +0 -65
- data/spec/higher_level_api/integration/queue_bind_spec.rb +0 -109
- data/spec/higher_level_api/integration/queue_declare_spec.rb +0 -190
- data/spec/higher_level_api/integration/queue_delete_spec.rb +0 -41
- data/spec/higher_level_api/integration/queue_purge_spec.rb +0 -30
- data/spec/higher_level_api/integration/queue_unbind_spec.rb +0 -54
- data/spec/higher_level_api/integration/read_only_consumer_spec.rb +0 -60
- data/spec/higher_level_api/integration/sender_selected_distribution_spec.rb +0 -36
- data/spec/higher_level_api/integration/tls_connection_spec.rb +0 -127
- data/spec/higher_level_api/integration/tx_commit_spec.rb +0 -21
- data/spec/higher_level_api/integration/tx_rollback_spec.rb +0 -21
- data/spec/higher_level_api/integration/with_channel_spec.rb +0 -25
- data/spec/issues/issue100_spec.rb +0 -42
- data/spec/issues/issue141_spec.rb +0 -44
- data/spec/issues/issue78_spec.rb +0 -75
- data/spec/issues/issue83_spec.rb +0 -31
- data/spec/issues/issue97_attachment.json +0 -1
- data/spec/issues/issue97_spec.rb +0 -176
- data/spec/lower_level_api/integration/basic_cancel_spec.rb +0 -69
- data/spec/lower_level_api/integration/basic_consume_spec.rb +0 -100
- data/spec/spec_helper.rb +0 -64
- data/spec/stress/channel_open_stress_spec.rb +0 -51
- data/spec/stress/channel_open_stress_with_single_threaded_connection_spec.rb +0 -28
- data/spec/stress/concurrent_consumers_stress_spec.rb +0 -69
- data/spec/stress/concurrent_publishers_stress_spec.rb +0 -57
- data/spec/stress/connection_open_close_spec.rb +0 -40
- 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/client_key.pem +0 -27
- data/spec/tls/server_cert.pem +0 -18
- data/spec/tls/server_key.pem +0 -27
- data/spec/unit/bunny_spec.rb +0 -15
- data/spec/unit/concurrent/atomic_fixnum_spec.rb +0 -35
- data/spec/unit/concurrent/condition_spec.rb +0 -82
- data/spec/unit/concurrent/linked_continuation_queue_spec.rb +0 -35
- data/spec/unit/concurrent/synchronized_sorted_set_spec.rb +0 -73
- data/spec/unit/system_timer_spec.rb +0 -10
- data/spec/unit/version_delivery_tag_spec.rb +0 -28
data/lib/bunny/channel.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
require "thread"
|
3
5
|
require "monitor"
|
4
6
|
require "set"
|
@@ -13,11 +15,7 @@ require "bunny/delivery_info"
|
|
13
15
|
require "bunny/return_info"
|
14
16
|
require "bunny/message_properties"
|
15
17
|
|
16
|
-
|
17
|
-
require "bunny/concurrent/linked_continuation_queue"
|
18
|
-
else
|
19
|
-
require "bunny/concurrent/continuation_queue"
|
20
|
-
end
|
18
|
+
require "bunny/concurrent/continuation_queue"
|
21
19
|
|
22
20
|
module Bunny
|
23
21
|
# ## Channels in RabbitMQ
|
@@ -37,11 +35,10 @@ module Bunny
|
|
37
35
|
# Channels can be opened either via `Bunny::Session#create_channel` (sufficient in the majority
|
38
36
|
# of cases) or by instantiating `Bunny::Channel` directly:
|
39
37
|
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
# conn.start
|
38
|
+
# conn = Bunny.new
|
39
|
+
# conn.start
|
43
40
|
#
|
44
|
-
#
|
41
|
+
# ch = conn.create_channel
|
45
42
|
#
|
46
43
|
# This will automatically allocate a channel id.
|
47
44
|
#
|
@@ -51,10 +48,8 @@ module Bunny
|
|
51
48
|
# closed, too. Closed channels can no longer be used. Attempts to use them will raise
|
52
49
|
# {Bunny::ChannelAlreadyClosed}.
|
53
50
|
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
# ch = conn.create_channel
|
57
|
-
# ch.close
|
51
|
+
# ch = conn.create_channel
|
52
|
+
# ch.close
|
58
53
|
#
|
59
54
|
# ## Higher-level API
|
60
55
|
#
|
@@ -146,6 +141,10 @@ module Bunny
|
|
146
141
|
attr_reader :work_pool
|
147
142
|
# @return [Integer] Next publisher confirmations sequence index
|
148
143
|
attr_reader :next_publish_seq_no
|
144
|
+
# @return [Integer] Offset for the confirmations sequence index.
|
145
|
+
# This will be set to the current sequence index during automatic network failure recovery
|
146
|
+
# to keep the sequence monotonic for the user and abstract the reset from the protocol
|
147
|
+
attr_reader :delivery_tag_offset
|
149
148
|
# @return [Hash<String, Bunny::Queue>] Queue instances declared on this channel
|
150
149
|
attr_reader :queues
|
151
150
|
# @return [Hash<String, Bunny::Exchange>] Exchange instances declared on this channel
|
@@ -157,7 +156,15 @@ module Bunny
|
|
157
156
|
# @return [Hash<String, Bunny::Consumer>] Consumer instances declared on this channel
|
158
157
|
attr_reader :consumers
|
159
158
|
|
159
|
+
# @return [Integer] active basic.qos prefetch value
|
160
|
+
attr_reader :prefetch_count
|
161
|
+
# @return [Integer] active basic.qos prefetch global mode
|
162
|
+
attr_reader :prefetch_global
|
163
|
+
|
164
|
+
attr_reader :cancel_consumers_before_closing
|
165
|
+
|
160
166
|
DEFAULT_CONTENT_TYPE = "application/octet-stream".freeze
|
167
|
+
SHORTSTR_LIMIT = 255
|
161
168
|
|
162
169
|
# @param [Bunny::Session] connection AMQP 0.9.1 connection
|
163
170
|
# @param [Integer] id Channel id, pass nil to make Bunny automatically allocate it
|
@@ -166,6 +173,17 @@ module Bunny
|
|
166
173
|
@connection = connection
|
167
174
|
@logger = connection.logger
|
168
175
|
@id = id || @connection.next_channel_id
|
176
|
+
|
177
|
+
# channel allocator is exhausted
|
178
|
+
if @id < 0
|
179
|
+
msg = "Cannot open a channel: max number of channels on connection reached. Connection channel_max value: #{@connection.channel_max}"
|
180
|
+
@logger.error(msg)
|
181
|
+
|
182
|
+
raise msg
|
183
|
+
else
|
184
|
+
@logger.debug { "Allocated channel id: #{@id}" }
|
185
|
+
end
|
186
|
+
|
169
187
|
@status = :opening
|
170
188
|
|
171
189
|
@connection.register_channel(self)
|
@@ -179,6 +197,9 @@ module Bunny
|
|
179
197
|
@publishing_mutex = @connection.mutex_impl.new
|
180
198
|
@consumer_mutex = @connection.mutex_impl.new
|
181
199
|
|
200
|
+
@queue_mutex = @connection.mutex_impl.new
|
201
|
+
@exchange_mutex = @connection.mutex_impl.new
|
202
|
+
|
182
203
|
@unconfirmed_set_mutex = @connection.mutex_impl.new
|
183
204
|
|
184
205
|
self.reset_continuations
|
@@ -191,15 +212,21 @@ module Bunny
|
|
191
212
|
@threads_waiting_on_basic_get_continuations = Set.new
|
192
213
|
|
193
214
|
@next_publish_seq_no = 0
|
215
|
+
@delivery_tag_offset = 0
|
194
216
|
|
195
217
|
@recoveries_counter = Bunny::Concurrent::AtomicFixnum.new(0)
|
218
|
+
@uncaught_exception_handler = Proc.new do |e, consumer|
|
219
|
+
@logger.error "Uncaught exception from consumer #{consumer.to_s}: #{e.inspect} @ #{e.backtrace[0]}"
|
220
|
+
end
|
221
|
+
|
222
|
+
@cancel_consumers_before_closing = false
|
196
223
|
end
|
197
224
|
|
198
225
|
attr_reader :recoveries_counter
|
199
226
|
|
200
227
|
# @private
|
201
|
-
def
|
202
|
-
@connection.
|
228
|
+
def wait_on_continuations_timeout
|
229
|
+
@connection.transport_write_timeout
|
203
230
|
end
|
204
231
|
|
205
232
|
# Opens the channel and resets its internal state
|
@@ -223,8 +250,29 @@ module Bunny
|
|
223
250
|
# {Bunny::Queue}, {Bunny::Exchange} and {Bunny::Consumer} instances.
|
224
251
|
# @api public
|
225
252
|
def close
|
253
|
+
# see bunny#528
|
254
|
+
raise_if_no_longer_open!
|
255
|
+
|
256
|
+
# This is a best-effort attempt to cancel all consumers before closing the channel.
|
257
|
+
# Retries are extremely unlikely to succeed, and the channel itself is about to be closed,
|
258
|
+
# so we don't bother retrying.
|
259
|
+
if self.cancel_consumers_before_closing?
|
260
|
+
# cancelling a consumer involves using the same mutex, so avoid holding the lock
|
261
|
+
keys = @consumer_mutex.synchronize { @consumers.keys }
|
262
|
+
keys.each do |ctag|
|
263
|
+
begin
|
264
|
+
self.basic_cancel(ctag)
|
265
|
+
rescue Bunny::Exception
|
266
|
+
# ignore
|
267
|
+
rescue Bunny::ClientTimeout
|
268
|
+
# ignore
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
226
273
|
@connection.close_channel(self)
|
227
|
-
closed
|
274
|
+
@status = :closed
|
275
|
+
@work_pool.shutdown
|
228
276
|
maybe_kill_consumer_work_pool!
|
229
277
|
end
|
230
278
|
|
@@ -240,7 +288,6 @@ module Bunny
|
|
240
288
|
@status == :closed
|
241
289
|
end
|
242
290
|
|
243
|
-
|
244
291
|
#
|
245
292
|
# @group Backwards compatibility with 0.8.0
|
246
293
|
#
|
@@ -267,6 +314,23 @@ module Bunny
|
|
267
314
|
|
268
315
|
# @endgroup
|
269
316
|
|
317
|
+
# @group Other settings
|
318
|
+
|
319
|
+
def configure(&block)
|
320
|
+
block.call(self) if block_given?
|
321
|
+
|
322
|
+
self
|
323
|
+
end
|
324
|
+
|
325
|
+
def cancel_consumers_before_closing!
|
326
|
+
@cancel_consumers_before_closing = true
|
327
|
+
end
|
328
|
+
|
329
|
+
def cancel_consumers_before_closing?
|
330
|
+
!!@cancel_consumers_before_closing
|
331
|
+
end
|
332
|
+
|
333
|
+
# @endgroup
|
270
334
|
|
271
335
|
#
|
272
336
|
# Higher-level API, similar to amqp gem
|
@@ -289,7 +353,7 @@ module Bunny
|
|
289
353
|
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
|
290
354
|
# @api public
|
291
355
|
def fanout(name, opts = {})
|
292
|
-
Exchange.new(self, :fanout, name, opts)
|
356
|
+
find_exchange(name) || Exchange.new(self, :fanout, name, opts)
|
293
357
|
end
|
294
358
|
|
295
359
|
# Declares a direct exchange or looks it up in the cache of previously
|
@@ -307,7 +371,7 @@ module Bunny
|
|
307
371
|
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
|
308
372
|
# @api public
|
309
373
|
def direct(name, opts = {})
|
310
|
-
Exchange.new(self, :direct, name, opts)
|
374
|
+
find_exchange(name) || Exchange.new(self, :direct, name, opts)
|
311
375
|
end
|
312
376
|
|
313
377
|
# Declares a topic exchange or looks it up in the cache of previously
|
@@ -325,7 +389,7 @@ module Bunny
|
|
325
389
|
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
|
326
390
|
# @api public
|
327
391
|
def topic(name, opts = {})
|
328
|
-
Exchange.new(self, :topic, name, opts)
|
392
|
+
find_exchange(name) || Exchange.new(self, :topic, name, opts)
|
329
393
|
end
|
330
394
|
|
331
395
|
# Declares a headers exchange or looks it up in the cache of previously
|
@@ -343,14 +407,14 @@ module Bunny
|
|
343
407
|
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
|
344
408
|
# @api public
|
345
409
|
def headers(name, opts = {})
|
346
|
-
Exchange.new(self, :headers, name, opts)
|
410
|
+
find_exchange(name) || Exchange.new(self, :headers, name, opts)
|
347
411
|
end
|
348
412
|
|
349
413
|
# Provides access to the default exchange
|
350
414
|
# @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
|
351
415
|
# @api public
|
352
416
|
def default_exchange
|
353
|
-
|
417
|
+
@default_exchange ||= Exchange.default(self)
|
354
418
|
end
|
355
419
|
|
356
420
|
# Declares a headers exchange or looks it up in the cache of previously
|
@@ -368,7 +432,7 @@ module Bunny
|
|
368
432
|
# @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
|
369
433
|
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
|
370
434
|
def exchange(name, opts = {})
|
371
|
-
Exchange.new(self, opts.fetch(:type, :direct), name, opts)
|
435
|
+
find_exchange(name) || Exchange.new(self, opts.fetch(:type, :direct), name, opts)
|
372
436
|
end
|
373
437
|
|
374
438
|
# @endgroup
|
@@ -384,34 +448,105 @@ module Bunny
|
|
384
448
|
# @option opts [Boolean] :durable (false) Should this queue be durable?
|
385
449
|
# @option opts [Boolean] :auto-delete (false) Should this queue be automatically deleted when the last consumer disconnects?
|
386
450
|
# @option opts [Boolean] :exclusive (false) Should this queue be exclusive (only can be used by this connection, removed when the connection is closed)?
|
387
|
-
# @option opts [
|
451
|
+
# @option opts [Hash] :arguments ({}) Optional arguments (x-arguments)
|
388
452
|
#
|
389
453
|
# @return [Bunny::Queue] Queue that was declared or looked up in the cache
|
390
454
|
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
391
455
|
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
|
392
456
|
# @api public
|
393
457
|
def queue(name = AMQ::Protocol::EMPTY_STRING, opts = {})
|
458
|
+
throw ArgumentError.new("queue name must not be nil") if name.nil?
|
459
|
+
|
394
460
|
q = find_queue(name) || Bunny::Queue.new(self, name, opts)
|
395
461
|
|
396
462
|
register_queue(q)
|
397
463
|
end
|
398
464
|
|
399
|
-
#
|
465
|
+
# Declares a new client-named quorum queue.
|
466
|
+
#
|
467
|
+
# @param [String] name Queue name. Empty (server-generated) names are not supported by this method.
|
468
|
+
# @param [Hash] opts Queue properties and other options. Durability, exclusivity, auto-deletion options will be ignored.
|
469
|
+
#
|
470
|
+
# @option opts [Hash] :arguments ({}) Optional arguments (x-arguments)
|
471
|
+
#
|
472
|
+
# @return [Bunny::Queue] Queue that was declared
|
473
|
+
# @see #durable_queue
|
474
|
+
# @see #queue
|
475
|
+
# @api public
|
476
|
+
def quorum_queue(name, opts = {})
|
477
|
+
throw ArgumentError.new("quorum queue name must not be nil") if name.nil?
|
478
|
+
throw ArgumentError.new("quorum queue name must not be empty (server-named QQs do not make sense)") if name.empty?
|
400
479
|
|
480
|
+
durable_queue(name, Bunny::Queue::Types::QUORUM, opts)
|
481
|
+
end
|
401
482
|
|
402
|
-
#
|
483
|
+
# Declares a new client-named stream (that Bunny can use as if it was a queue).
|
484
|
+
# Note that Bunny would still use AMQP 0-9-1 to perform operations on this "queue".
|
485
|
+
# To use stream-specific operations and to gain from stream protocol efficiency and partitioning,
|
486
|
+
# use a Ruby client for the RabbitMQ stream protocol.
|
487
|
+
#
|
488
|
+
# @param [String] name Stream name. Empty (server-generated) names are not supported by this method.
|
489
|
+
# @param [Hash] opts Queue properties and other options. Durability, exclusivity, auto-deletion options will be ignored.
|
490
|
+
#
|
491
|
+
# @option opts [Hash] :arguments ({}) Optional arguments (x-arguments)
|
492
|
+
#
|
493
|
+
#
|
494
|
+
# @return [Bunny::Queue] Queue that was declared
|
495
|
+
# @see #durable_queue
|
496
|
+
# @see #queue
|
497
|
+
# @api public
|
498
|
+
def stream(name, opts = {})
|
499
|
+
throw ArgumentError.new("stream name must not be nil") if name.nil?
|
500
|
+
throw ArgumentError.new("stream name must not be empty (server-named QQs do not make sense)") if name.empty?
|
501
|
+
|
502
|
+
durable_queue(name, Bunny::Queue::Types::STREAM, opts)
|
503
|
+
end
|
403
504
|
|
404
|
-
#
|
405
|
-
#
|
505
|
+
# Declares a new server-named queue that is automatically deleted when the
|
506
|
+
# connection is closed.
|
406
507
|
#
|
407
|
-
# @param [
|
408
|
-
# @
|
409
|
-
#
|
508
|
+
# @param [String] name Queue name. Empty (server-generated) names are not supported by this method.
|
509
|
+
# @param [Hash] opts Queue properties and other options. Durability, exclusivity, auto-deletion options will be ignored.
|
510
|
+
#
|
511
|
+
# @option opts [Hash] :arguments ({}) Optional arguments (x-arguments)
|
512
|
+
#
|
513
|
+
# @return [Bunny::Queue] Queue that was declared
|
514
|
+
# @see #queue
|
410
515
|
# @api public
|
411
|
-
def
|
412
|
-
|
516
|
+
def durable_queue(name, type = "classic", opts = {})
|
517
|
+
throw ArgumentError.new("queue name must not be nil") if name.nil?
|
518
|
+
throw ArgumentError.new("queue name must not be empty (server-named durable queues do not make sense)") if name.empty?
|
519
|
+
|
520
|
+
final_opts = opts.merge({
|
521
|
+
:type => type,
|
522
|
+
:durable => true,
|
523
|
+
# exclusive or auto-delete QQs do not make much sense
|
524
|
+
:exclusive => false,
|
525
|
+
:auto_delete => false
|
526
|
+
})
|
527
|
+
q = find_queue(name) || Bunny::Queue.new(self, name, final_opts)
|
528
|
+
|
529
|
+
register_queue(q)
|
413
530
|
end
|
414
531
|
|
532
|
+
# Declares a new server-named queue that is automatically deleted when the
|
533
|
+
# connection is closed.
|
534
|
+
#
|
535
|
+
# @return [Bunny::Queue] Queue that was declared
|
536
|
+
# @see #queue
|
537
|
+
# @api public
|
538
|
+
def temporary_queue(opts = {})
|
539
|
+
temporary_queue_opts = {
|
540
|
+
:exclusive => true
|
541
|
+
}
|
542
|
+
queue("", opts.merge(temporary_queue_opts))
|
543
|
+
end
|
544
|
+
|
545
|
+
# @endgroup
|
546
|
+
|
547
|
+
|
548
|
+
# @group QoS and Flow Control
|
549
|
+
|
415
550
|
# Flow control. When set to false, RabbitMQ will stop delivering messages on this
|
416
551
|
# channel.
|
417
552
|
#
|
@@ -444,9 +579,7 @@ module Bunny
|
|
444
579
|
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
445
580
|
# @api public
|
446
581
|
def reject(delivery_tag, requeue = false)
|
447
|
-
|
448
|
-
basic_reject(delivery_tag.to_i, requeue)
|
449
|
-
end
|
582
|
+
basic_reject(delivery_tag.to_i, requeue)
|
450
583
|
end
|
451
584
|
|
452
585
|
# Acknowledges a message. Acknowledged messages are completely removed from the queue.
|
@@ -457,9 +590,7 @@ module Bunny
|
|
457
590
|
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
458
591
|
# @api public
|
459
592
|
def ack(delivery_tag, multiple = false)
|
460
|
-
|
461
|
-
basic_ack(delivery_tag.to_i, multiple)
|
462
|
-
end
|
593
|
+
basic_ack(delivery_tag.to_i, multiple)
|
463
594
|
end
|
464
595
|
alias acknowledge ack
|
465
596
|
|
@@ -474,9 +605,7 @@ module Bunny
|
|
474
605
|
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
475
606
|
# @api public
|
476
607
|
def nack(delivery_tag, multiple = false, requeue = false)
|
477
|
-
|
478
|
-
basic_nack(delivery_tag.to_i, multiple, requeue)
|
479
|
-
end
|
608
|
+
basic_nack(delivery_tag.to_i, multiple, requeue)
|
480
609
|
end
|
481
610
|
|
482
611
|
# @endgroup
|
@@ -513,6 +642,7 @@ module Bunny
|
|
513
642
|
# @api public
|
514
643
|
def basic_publish(payload, exchange, routing_key, opts = {})
|
515
644
|
raise_if_no_longer_open!
|
645
|
+
raise ArgumentError, "routing key cannot be longer than #{SHORTSTR_LIMIT} characters" if routing_key && routing_key.size > SHORTSTR_LIMIT
|
516
646
|
|
517
647
|
exchange_name = if exchange.respond_to?(:name)
|
518
648
|
exchange.name
|
@@ -531,8 +661,10 @@ module Bunny
|
|
531
661
|
opts[:priority] ||= 0
|
532
662
|
|
533
663
|
if @next_publish_seq_no > 0
|
534
|
-
@
|
535
|
-
|
664
|
+
@unconfirmed_set_mutex.synchronize do
|
665
|
+
@unconfirmed_set.add(@next_publish_seq_no)
|
666
|
+
@next_publish_seq_no += 1
|
667
|
+
end
|
536
668
|
end
|
537
669
|
|
538
670
|
frames = AMQ::Protocol::Basic::Publish.encode(@id,
|
@@ -543,7 +675,7 @@ module Bunny
|
|
543
675
|
opts[:mandatory],
|
544
676
|
false,
|
545
677
|
@connection.frame_max)
|
546
|
-
@connection.
|
678
|
+
@connection.send_frameset(frames, self)
|
547
679
|
|
548
680
|
self
|
549
681
|
end
|
@@ -555,7 +687,8 @@ module Bunny
|
|
555
687
|
# @param [String] queue Queue name
|
556
688
|
# @param [Hash] opts Options
|
557
689
|
#
|
558
|
-
# @option opts [Boolean] :ack (true)
|
690
|
+
# @option opts [Boolean] :ack (true) [DEPRECATED] Use :manual_ack instead
|
691
|
+
# @option opts [Boolean] :manual_ack (true) Will this message be acknowledged manually?
|
559
692
|
#
|
560
693
|
# @return [Array] A triple of delivery info, message properties and message content
|
561
694
|
#
|
@@ -564,15 +697,20 @@ module Bunny
|
|
564
697
|
# conn.start
|
565
698
|
# ch = conn.create_channel
|
566
699
|
# # here we assume the queue already exists and has messages
|
567
|
-
# delivery_info, properties, payload = ch.basic_get("bunny.examples.queue1", :
|
700
|
+
# delivery_info, properties, payload = ch.basic_get("bunny.examples.queue1", :manual_ack => true)
|
568
701
|
# ch.acknowledge(delivery_info.delivery_tag)
|
569
702
|
# @see Bunny::Queue#pop
|
570
703
|
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
571
704
|
# @api public
|
572
|
-
def basic_get(queue, opts = {:
|
705
|
+
def basic_get(queue, opts = {:manual_ack => true})
|
573
706
|
raise_if_no_longer_open!
|
574
707
|
|
575
|
-
|
708
|
+
unless opts[:ack].nil?
|
709
|
+
warn "[DEPRECATION] `:ack` is deprecated. Please use `:manual_ack` instead."
|
710
|
+
opts[:manual_ack] = opts[:ack]
|
711
|
+
end
|
712
|
+
|
713
|
+
@connection.send_frame(AMQ::Protocol::Basic::Get.encode(@id, queue, !(opts[:manual_ack])))
|
576
714
|
# this is a workaround for the edge case when basic_get is called in a tight loop
|
577
715
|
# and network goes down we need to perform recovery. The problem is, basic_get will
|
578
716
|
# keep blocking the thread that calls it without clear way to constantly unblock it
|
@@ -580,40 +718,59 @@ module Bunny
|
|
580
718
|
# implementation (and even more correct and convenient ones, such as wait/notify, should
|
581
719
|
# we implement them). So we return a triple of nils immediately which apps should be
|
582
720
|
# able to handle anyway as "got no message, no need to act". MK.
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
721
|
+
last_basic_get_response = if @connection.open?
|
722
|
+
begin
|
723
|
+
wait_on_basic_get_continuations
|
724
|
+
rescue Timeout::Error => e
|
725
|
+
raise_if_continuation_resulted_in_a_channel_error!
|
726
|
+
raise e
|
727
|
+
end
|
728
|
+
else
|
729
|
+
[nil, nil, nil]
|
730
|
+
end
|
588
731
|
|
589
732
|
raise_if_continuation_resulted_in_a_channel_error!
|
590
|
-
|
733
|
+
last_basic_get_response
|
591
734
|
end
|
592
735
|
|
736
|
+
# prefetch_count is of type short in the protocol. MK.
|
737
|
+
MAX_PREFETCH_COUNT = (2 ** 16) - 1
|
738
|
+
|
593
739
|
# Controls message delivery rate using basic.qos AMQP 0.9.1 method.
|
594
740
|
#
|
595
741
|
# @param [Integer] prefetch_count How many messages can consumers on this channel be given at a time
|
596
742
|
# (before they have to acknowledge or reject one of the earlier received messages)
|
597
|
-
# @param [Boolean] global
|
743
|
+
# @param [Boolean] global
|
744
|
+
# Whether to use global mode for prefetch:
|
745
|
+
# - +false+: per-consumer
|
746
|
+
# - +true+: per-channel
|
747
|
+
# Note that the default value (+false+) hasn't actually changed, but
|
748
|
+
# previous documentation described that as meaning per-channel and
|
749
|
+
# unsupported in RabbitMQ, whereas it now actually appears to mean
|
750
|
+
# per-consumer and supported
|
751
|
+
# (https://www.rabbitmq.com/consumer-prefetch.html).
|
598
752
|
# @return [AMQ::Protocol::Basic::QosOk] RabbitMQ response
|
599
753
|
# @see Bunny::Channel#prefetch
|
600
754
|
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
601
755
|
# @api public
|
602
|
-
def basic_qos(
|
603
|
-
raise ArgumentError.new("prefetch count must be a positive integer, given: #{
|
756
|
+
def basic_qos(count, global = false)
|
757
|
+
raise ArgumentError.new("prefetch count must be a positive integer, given: #{count}") if count < 0
|
758
|
+
raise ArgumentError.new("prefetch count must be no greater than #{MAX_PREFETCH_COUNT}, given: #{count}") if count > MAX_PREFETCH_COUNT
|
604
759
|
raise_if_no_longer_open!
|
605
760
|
|
606
|
-
@connection.send_frame(AMQ::Protocol::Basic::Qos.encode(@id, 0,
|
761
|
+
@connection.send_frame(AMQ::Protocol::Basic::Qos.encode(@id, 0, count, global))
|
607
762
|
|
608
|
-
|
763
|
+
with_continuation_timeout do
|
609
764
|
@last_basic_qos_ok = wait_on_continuations
|
610
765
|
end
|
611
766
|
raise_if_continuation_resulted_in_a_channel_error!
|
612
767
|
|
613
|
-
@prefetch_count
|
768
|
+
@prefetch_count = count
|
769
|
+
@prefetch_global = global
|
614
770
|
|
615
771
|
@last_basic_qos_ok
|
616
772
|
end
|
773
|
+
alias prefetch basic_qos
|
617
774
|
|
618
775
|
# Redeliver unacknowledged messages
|
619
776
|
#
|
@@ -624,7 +781,7 @@ module Bunny
|
|
624
781
|
raise_if_no_longer_open!
|
625
782
|
|
626
783
|
@connection.send_frame(AMQ::Protocol::Basic::Recover.encode(@id, requeue))
|
627
|
-
|
784
|
+
with_continuation_timeout do
|
628
785
|
@last_basic_recover_ok = wait_on_continuations
|
629
786
|
end
|
630
787
|
raise_if_continuation_resulted_in_a_channel_error!
|
@@ -654,7 +811,7 @@ module Bunny
|
|
654
811
|
#
|
655
812
|
# ch = conn.create_channel
|
656
813
|
# q.subscribe do |delivery_info, properties, payload|
|
657
|
-
# #
|
814
|
+
# # reject the message
|
658
815
|
# ch.basic_reject(delivery_info.delivery_tag, false)
|
659
816
|
# end
|
660
817
|
#
|
@@ -664,17 +821,19 @@ module Bunny
|
|
664
821
|
#
|
665
822
|
# ch = conn.create_channel
|
666
823
|
# # we assume the queue exists and has messages
|
667
|
-
# delivery_info, properties, payload = ch.basic_get("bunny.examples.queue3", :
|
824
|
+
# delivery_info, properties, payload = ch.basic_get("bunny.examples.queue3", :manual_ack => true)
|
668
825
|
# ch.basic_reject(delivery_info.delivery_tag, true)
|
669
826
|
#
|
670
827
|
# @see Bunny::Channel#basic_nack
|
671
828
|
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
672
829
|
# @api public
|
673
|
-
def basic_reject(delivery_tag, requeue)
|
674
|
-
|
675
|
-
|
830
|
+
def basic_reject(delivery_tag, requeue = false)
|
831
|
+
guarding_against_stale_delivery_tags(delivery_tag) do
|
832
|
+
raise_if_no_longer_open!
|
833
|
+
@connection.send_frame(AMQ::Protocol::Basic::Reject.encode(@id, delivery_tag, requeue))
|
676
834
|
|
677
|
-
|
835
|
+
nil
|
836
|
+
end
|
678
837
|
end
|
679
838
|
|
680
839
|
# Acknowledges a delivery (message).
|
@@ -690,7 +849,7 @@ module Bunny
|
|
690
849
|
# ch = conn.create_channel
|
691
850
|
# q.subscribe do |delivery_info, properties, payload|
|
692
851
|
# # requeue the message
|
693
|
-
# ch.basic_ack(delivery_info.delivery_tag)
|
852
|
+
# ch.basic_ack(delivery_info.delivery_tag.to_i)
|
694
853
|
# end
|
695
854
|
#
|
696
855
|
# @example Ack a message fetched via basic.get
|
@@ -699,8 +858,8 @@ module Bunny
|
|
699
858
|
#
|
700
859
|
# ch = conn.create_channel
|
701
860
|
# # we assume the queue exists and has messages
|
702
|
-
# delivery_info, properties, payload = ch.basic_get("bunny.examples.queue3", :
|
703
|
-
# ch.basic_ack(delivery_info.delivery_tag)
|
861
|
+
# delivery_info, properties, payload = ch.basic_get("bunny.examples.queue3", :manual_ack => true)
|
862
|
+
# ch.basic_ack(delivery_info.delivery_tag.to_i)
|
704
863
|
#
|
705
864
|
# @example Ack multiple messages fetched via basic.get
|
706
865
|
# conn = Bunny.new
|
@@ -708,20 +867,21 @@ module Bunny
|
|
708
867
|
#
|
709
868
|
# ch = conn.create_channel
|
710
869
|
# # we assume the queue exists and has messages
|
711
|
-
# _, _, payload1 = ch.basic_get("bunny.examples.queue3", :
|
712
|
-
# _, _, payload2 = ch.basic_get("bunny.examples.queue3", :
|
713
|
-
# delivery_info, properties, payload3 = ch.basic_get("bunny.examples.queue3", :
|
870
|
+
# _, _, payload1 = ch.basic_get("bunny.examples.queue3", :manual_ack => true)
|
871
|
+
# _, _, payload2 = ch.basic_get("bunny.examples.queue3", :manual_ack => true)
|
872
|
+
# delivery_info, properties, payload3 = ch.basic_get("bunny.examples.queue3", :manual_ack => true)
|
714
873
|
# # ack all fetched messages up to payload3
|
715
|
-
# ch.basic_ack(delivery_info.delivery_tag, true)
|
874
|
+
# ch.basic_ack(delivery_info.delivery_tag.to_i, true)
|
716
875
|
#
|
717
876
|
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
718
|
-
# @see #basic_ack_known_delivery_tag
|
719
877
|
# @api public
|
720
|
-
def basic_ack(delivery_tag, multiple)
|
721
|
-
|
722
|
-
|
878
|
+
def basic_ack(delivery_tag, multiple = false)
|
879
|
+
guarding_against_stale_delivery_tags(delivery_tag) do
|
880
|
+
raise_if_no_longer_open!
|
881
|
+
@connection.send_frame(AMQ::Protocol::Basic::Ack.encode(@id, delivery_tag, multiple))
|
723
882
|
|
724
|
-
|
883
|
+
nil
|
884
|
+
end
|
725
885
|
end
|
726
886
|
|
727
887
|
# Rejects or requeues messages just like {Bunny::Channel#basic_reject} but can do so
|
@@ -758,7 +918,7 @@ module Bunny
|
|
758
918
|
#
|
759
919
|
# ch = conn.create_channel
|
760
920
|
# # we assume the queue exists and has messages
|
761
|
-
# delivery_info, properties, payload = ch.basic_get("bunny.examples.queue3", :
|
921
|
+
# delivery_info, properties, payload = ch.basic_get("bunny.examples.queue3", :manual_ack => true)
|
762
922
|
# ch.basic_nack(delivery_info.delivery_tag, false, true)
|
763
923
|
#
|
764
924
|
#
|
@@ -768,9 +928,9 @@ module Bunny
|
|
768
928
|
#
|
769
929
|
# ch = conn.create_channel
|
770
930
|
# # we assume the queue exists and has messages
|
771
|
-
# _, _, payload1 = ch.basic_get("bunny.examples.queue3", :
|
772
|
-
# _, _, payload2 = ch.basic_get("bunny.examples.queue3", :
|
773
|
-
# delivery_info, properties, payload3 = ch.basic_get("bunny.examples.queue3", :
|
931
|
+
# _, _, payload1 = ch.basic_get("bunny.examples.queue3", :manual_ack => true)
|
932
|
+
# _, _, payload2 = ch.basic_get("bunny.examples.queue3", :manual_ack => true)
|
933
|
+
# delivery_info, properties, payload3 = ch.basic_get("bunny.examples.queue3", :manual_ack => true)
|
774
934
|
# # requeue all fetched messages up to payload3
|
775
935
|
# ch.basic_nack(delivery_info.delivery_tag, true, true)
|
776
936
|
#
|
@@ -778,13 +938,15 @@ module Bunny
|
|
778
938
|
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
|
779
939
|
# @api public
|
780
940
|
def basic_nack(delivery_tag, multiple = false, requeue = false)
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
941
|
+
guarding_against_stale_delivery_tags(delivery_tag) do
|
942
|
+
raise_if_no_longer_open!
|
943
|
+
@connection.send_frame(AMQ::Protocol::Basic::Nack.encode(@id,
|
944
|
+
delivery_tag,
|
945
|
+
multiple,
|
946
|
+
requeue))
|
786
947
|
|
787
|
-
|
948
|
+
nil
|
949
|
+
end
|
788
950
|
end
|
789
951
|
|
790
952
|
# Registers a consumer for queue. Delivered messages will be handled with the block
|
@@ -826,7 +988,7 @@ module Bunny
|
|
826
988
|
arguments))
|
827
989
|
|
828
990
|
begin
|
829
|
-
|
991
|
+
with_continuation_timeout do
|
830
992
|
@last_basic_consume_ok = wait_on_continuations
|
831
993
|
end
|
832
994
|
rescue Exception => e
|
@@ -876,7 +1038,7 @@ module Bunny
|
|
876
1038
|
consumer.arguments))
|
877
1039
|
|
878
1040
|
begin
|
879
|
-
|
1041
|
+
with_continuation_timeout do
|
880
1042
|
@last_basic_consume_ok = wait_on_continuations
|
881
1043
|
end
|
882
1044
|
rescue Exception => e
|
@@ -904,18 +1066,29 @@ module Bunny
|
|
904
1066
|
# it was on is auto-deleted and this consumer was the last one, the queue will be deleted.
|
905
1067
|
#
|
906
1068
|
# @param [String] consumer_tag Consumer tag (unique identifier) to cancel
|
1069
|
+
# @param [Hash] arguments ({}) Optional arguments
|
907
1070
|
#
|
908
|
-
# @
|
1071
|
+
# @option opts [Boolean] :no_wait (false) if set to true, this method won't receive a response and will
|
1072
|
+
# immediately return nil
|
1073
|
+
#
|
1074
|
+
# @return [AMQ::Protocol::Basic::CancelOk] RabbitMQ response or nil, if the no_wait option is used
|
909
1075
|
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
910
1076
|
# @api public
|
911
|
-
def basic_cancel(consumer_tag)
|
912
|
-
|
1077
|
+
def basic_cancel(consumer_tag, opts = {})
|
1078
|
+
no_wait = opts.fetch(:no_wait, false)
|
1079
|
+
@connection.send_frame(AMQ::Protocol::Basic::Cancel.encode(@id, consumer_tag, no_wait))
|
913
1080
|
|
914
|
-
|
915
|
-
@last_basic_cancel_ok =
|
1081
|
+
if no_wait
|
1082
|
+
@last_basic_cancel_ok = nil
|
1083
|
+
else
|
1084
|
+
with_continuation_timeout do
|
1085
|
+
@last_basic_cancel_ok = wait_on_continuations
|
1086
|
+
end
|
916
1087
|
end
|
917
1088
|
|
918
|
-
|
1089
|
+
# reduces thread usage for channels that don't have any
|
1090
|
+
# consumers
|
1091
|
+
@work_pool.shutdown(true) unless self.any_consumers?
|
919
1092
|
|
920
1093
|
@last_basic_cancel_ok
|
921
1094
|
end
|
@@ -933,7 +1106,8 @@ module Bunny
|
|
933
1106
|
|
934
1107
|
# Declares a queue using queue.declare AMQP 0.9.1 method.
|
935
1108
|
#
|
936
|
-
# @param [String] name
|
1109
|
+
# @param [String] name The name of the queue or an empty string to let RabbitMQ generate a name.
|
1110
|
+
# Note that LF and CR characters will be stripped from the value.
|
937
1111
|
# @param [Hash] opts Queue properties
|
938
1112
|
#
|
939
1113
|
# @option opts [Boolean] durable (false) Should information about this queue be persisted to disk so that it
|
@@ -944,23 +1118,37 @@ module Bunny
|
|
944
1118
|
# connection is closed
|
945
1119
|
# @option opts [Boolean] passive (false) If true, queue will be checked for existence. If it does not
|
946
1120
|
# exist, {Bunny::NotFound} will be raised.
|
947
|
-
#
|
1121
|
+
# @option opts [Hash] :arguments ({}) Optional queue arguments (x-arguments)
|
948
1122
|
# @return [AMQ::Protocol::Queue::DeclareOk] RabbitMQ response
|
949
1123
|
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
950
1124
|
# @api public
|
951
1125
|
def queue_declare(name, opts = {})
|
952
1126
|
raise_if_no_longer_open!
|
953
1127
|
|
954
|
-
|
955
|
-
|
1128
|
+
Bunny::Queue.verify_type!(opts[:arguments]) if opts[:arguments]
|
1129
|
+
|
1130
|
+
# strip trailing new line and carriage returns
|
1131
|
+
# just like RabbitMQ does
|
1132
|
+
safe_name = name.gsub(/[\r\n]/, "")
|
1133
|
+
@pending_queue_declare_name = safe_name
|
1134
|
+
@connection.send_frame(
|
1135
|
+
AMQ::Protocol::Queue::Declare.encode(@id,
|
1136
|
+
@pending_queue_declare_name,
|
956
1137
|
opts.fetch(:passive, false),
|
957
1138
|
opts.fetch(:durable, false),
|
958
1139
|
opts.fetch(:exclusive, false),
|
959
1140
|
opts.fetch(:auto_delete, false),
|
960
1141
|
false,
|
961
1142
|
opts[:arguments]))
|
962
|
-
@last_queue_declare_ok = wait_on_continuations
|
963
1143
|
|
1144
|
+
begin
|
1145
|
+
with_continuation_timeout do
|
1146
|
+
@last_queue_declare_ok = wait_on_continuations
|
1147
|
+
end
|
1148
|
+
ensure
|
1149
|
+
# clear pending continuation context if it belongs to us
|
1150
|
+
@pending_queue_declare_name = nil if @pending_queue_declare_name == safe_name
|
1151
|
+
end
|
964
1152
|
raise_if_continuation_resulted_in_a_channel_error!
|
965
1153
|
|
966
1154
|
@last_queue_declare_ok
|
@@ -985,7 +1173,7 @@ module Bunny
|
|
985
1173
|
opts[:if_unused],
|
986
1174
|
opts[:if_empty],
|
987
1175
|
false))
|
988
|
-
|
1176
|
+
with_continuation_timeout do
|
989
1177
|
@last_queue_delete_ok = wait_on_continuations
|
990
1178
|
end
|
991
1179
|
raise_if_continuation_resulted_in_a_channel_error!
|
@@ -1005,7 +1193,7 @@ module Bunny
|
|
1005
1193
|
|
1006
1194
|
@connection.send_frame(AMQ::Protocol::Queue::Purge.encode(@id, name, false))
|
1007
1195
|
|
1008
|
-
|
1196
|
+
with_continuation_timeout do
|
1009
1197
|
@last_queue_purge_ok = wait_on_continuations
|
1010
1198
|
end
|
1011
1199
|
raise_if_continuation_resulted_in_a_channel_error!
|
@@ -1038,10 +1226,10 @@ module Bunny
|
|
1038
1226
|
@connection.send_frame(AMQ::Protocol::Queue::Bind.encode(@id,
|
1039
1227
|
name,
|
1040
1228
|
exchange_name,
|
1041
|
-
opts[:routing_key],
|
1229
|
+
(opts[:routing_key] || opts[:key]),
|
1042
1230
|
false,
|
1043
1231
|
opts[:arguments]))
|
1044
|
-
|
1232
|
+
with_continuation_timeout do
|
1045
1233
|
@last_queue_bind_ok = wait_on_continuations
|
1046
1234
|
end
|
1047
1235
|
|
@@ -1076,7 +1264,7 @@ module Bunny
|
|
1076
1264
|
exchange_name,
|
1077
1265
|
opts[:routing_key],
|
1078
1266
|
opts[:arguments]))
|
1079
|
-
|
1267
|
+
with_continuation_timeout do
|
1080
1268
|
@last_queue_unbind_ok = wait_on_continuations
|
1081
1269
|
end
|
1082
1270
|
|
@@ -1089,33 +1277,38 @@ module Bunny
|
|
1089
1277
|
|
1090
1278
|
# @group Exchange operations (exchange.*)
|
1091
1279
|
|
1092
|
-
# Declares a
|
1280
|
+
# Declares a exchange using exchange.declare AMQP 0.9.1 method.
|
1093
1281
|
#
|
1094
|
-
# @param [String] name
|
1282
|
+
# @param [String] name The name of the exchange. Note that LF and CR characters
|
1283
|
+
# will be stripped from the value.
|
1284
|
+
# @param [String,Symbol] type Exchange type, e.g. :fanout or :topic
|
1095
1285
|
# @param [Hash] opts Exchange properties
|
1096
1286
|
#
|
1097
|
-
# @option opts [Boolean] durable (false) Should information about this
|
1287
|
+
# @option opts [Boolean] durable (false) Should information about this exchange be persisted to disk so that it
|
1098
1288
|
# can survive broker restarts? Typically set to true for long-lived exchanges.
|
1099
|
-
# @option opts [Boolean] auto_delete (false) Should this
|
1289
|
+
# @option opts [Boolean] auto_delete (false) Should this exchange be deleted when it is no longer used?
|
1100
1290
|
# @option opts [Boolean] passive (false) If true, exchange will be checked for existence. If it does not
|
1101
1291
|
# exist, {Bunny::NotFound} will be raised.
|
1102
1292
|
#
|
1103
1293
|
# @return [AMQ::Protocol::Exchange::DeclareOk] RabbitMQ response
|
1104
|
-
# @see http://rubybunny.info/articles/
|
1294
|
+
# @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
|
1105
1295
|
# @api public
|
1106
1296
|
def exchange_declare(name, type, opts = {})
|
1107
1297
|
raise_if_no_longer_open!
|
1108
1298
|
|
1299
|
+
# strip trailing new line and carriage returns
|
1300
|
+
# just like RabbitMQ does
|
1301
|
+
safe_name = name.gsub(/[\r\n]/, "")
|
1109
1302
|
@connection.send_frame(AMQ::Protocol::Exchange::Declare.encode(@id,
|
1110
|
-
|
1303
|
+
safe_name,
|
1111
1304
|
type.to_s,
|
1112
1305
|
opts.fetch(:passive, false),
|
1113
1306
|
opts.fetch(:durable, false),
|
1114
1307
|
opts.fetch(:auto_delete, false),
|
1115
|
-
false,
|
1116
|
-
false,
|
1308
|
+
opts.fetch(:internal, false),
|
1309
|
+
opts.fetch(:no_wait, false),
|
1117
1310
|
opts[:arguments]))
|
1118
|
-
|
1311
|
+
with_continuation_timeout do
|
1119
1312
|
@last_exchange_declare_ok = wait_on_continuations
|
1120
1313
|
end
|
1121
1314
|
|
@@ -1140,7 +1333,7 @@ module Bunny
|
|
1140
1333
|
name,
|
1141
1334
|
opts[:if_unused],
|
1142
1335
|
false))
|
1143
|
-
|
1336
|
+
with_continuation_timeout do
|
1144
1337
|
@last_exchange_delete_ok = wait_on_continuations
|
1145
1338
|
end
|
1146
1339
|
|
@@ -1184,7 +1377,7 @@ module Bunny
|
|
1184
1377
|
opts[:routing_key],
|
1185
1378
|
false,
|
1186
1379
|
opts[:arguments]))
|
1187
|
-
|
1380
|
+
with_continuation_timeout do
|
1188
1381
|
@last_exchange_bind_ok = wait_on_continuations
|
1189
1382
|
end
|
1190
1383
|
|
@@ -1228,7 +1421,7 @@ module Bunny
|
|
1228
1421
|
opts[:routing_key],
|
1229
1422
|
false,
|
1230
1423
|
opts[:arguments]))
|
1231
|
-
|
1424
|
+
with_continuation_timeout do
|
1232
1425
|
@last_exchange_unbind_ok = wait_on_continuations
|
1233
1426
|
end
|
1234
1427
|
|
@@ -1256,7 +1449,7 @@ module Bunny
|
|
1256
1449
|
raise_if_no_longer_open!
|
1257
1450
|
|
1258
1451
|
@connection.send_frame(AMQ::Protocol::Channel::Flow.encode(@id, active))
|
1259
|
-
|
1452
|
+
with_continuation_timeout do
|
1260
1453
|
@last_channel_flow_ok = wait_on_continuations
|
1261
1454
|
end
|
1262
1455
|
raise_if_continuation_resulted_in_a_channel_error!
|
@@ -1277,10 +1470,11 @@ module Bunny
|
|
1277
1470
|
raise_if_no_longer_open!
|
1278
1471
|
|
1279
1472
|
@connection.send_frame(AMQ::Protocol::Tx::Select.encode(@id))
|
1280
|
-
|
1473
|
+
with_continuation_timeout do
|
1281
1474
|
@last_tx_select_ok = wait_on_continuations
|
1282
1475
|
end
|
1283
1476
|
raise_if_continuation_resulted_in_a_channel_error!
|
1477
|
+
@tx_mode = true
|
1284
1478
|
|
1285
1479
|
@last_tx_select_ok
|
1286
1480
|
end
|
@@ -1292,7 +1486,7 @@ module Bunny
|
|
1292
1486
|
raise_if_no_longer_open!
|
1293
1487
|
|
1294
1488
|
@connection.send_frame(AMQ::Protocol::Tx::Commit.encode(@id))
|
1295
|
-
|
1489
|
+
with_continuation_timeout do
|
1296
1490
|
@last_tx_commit_ok = wait_on_continuations
|
1297
1491
|
end
|
1298
1492
|
raise_if_continuation_resulted_in_a_channel_error!
|
@@ -1307,7 +1501,7 @@ module Bunny
|
|
1307
1501
|
raise_if_no_longer_open!
|
1308
1502
|
|
1309
1503
|
@connection.send_frame(AMQ::Protocol::Tx::Rollback.encode(@id))
|
1310
|
-
|
1504
|
+
with_continuation_timeout do
|
1311
1505
|
@last_tx_rollback_ok = wait_on_continuations
|
1312
1506
|
end
|
1313
1507
|
raise_if_continuation_resulted_in_a_channel_error!
|
@@ -1315,6 +1509,11 @@ module Bunny
|
|
1315
1509
|
@last_tx_rollback_ok
|
1316
1510
|
end
|
1317
1511
|
|
1512
|
+
# @return [Boolean] true if this channel has transactions enabled
|
1513
|
+
def using_tx?
|
1514
|
+
!!@tx_mode
|
1515
|
+
end
|
1516
|
+
|
1318
1517
|
# @endgroup
|
1319
1518
|
|
1320
1519
|
|
@@ -1326,6 +1525,7 @@ module Bunny
|
|
1326
1525
|
def using_publisher_confirmations?
|
1327
1526
|
@next_publish_seq_no > 0
|
1328
1527
|
end
|
1528
|
+
alias using_publisher_confirms? using_publisher_confirmations?
|
1329
1529
|
|
1330
1530
|
# Enables publisher confirms for the channel.
|
1331
1531
|
# @return [AMQ::Protocol::Confirm::SelectOk] RabbitMQ response
|
@@ -1334,7 +1534,7 @@ module Bunny
|
|
1334
1534
|
# @see #nacked_set
|
1335
1535
|
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
|
1336
1536
|
# @api public
|
1337
|
-
def confirm_select(callback=nil)
|
1537
|
+
def confirm_select(callback = nil)
|
1338
1538
|
raise_if_no_longer_open!
|
1339
1539
|
|
1340
1540
|
if @next_publish_seq_no == 0
|
@@ -1342,12 +1542,13 @@ module Bunny
|
|
1342
1542
|
@unconfirmed_set = Set.new
|
1343
1543
|
@nacked_set = Set.new
|
1344
1544
|
@next_publish_seq_no = 1
|
1545
|
+
@only_acks_received = true
|
1345
1546
|
end
|
1346
1547
|
|
1347
1548
|
@confirms_callback = callback
|
1348
1549
|
|
1349
1550
|
@connection.send_frame(AMQ::Protocol::Confirm::Select.encode(@id, false))
|
1350
|
-
|
1551
|
+
with_continuation_timeout do
|
1351
1552
|
@last_confirm_select_ok = wait_on_continuations
|
1352
1553
|
end
|
1353
1554
|
raise_if_continuation_resulted_in_a_channel_error!
|
@@ -1355,19 +1556,18 @@ module Bunny
|
|
1355
1556
|
end
|
1356
1557
|
|
1357
1558
|
# Blocks calling thread until confirms are received for all
|
1358
|
-
# currently unacknowledged published messages.
|
1559
|
+
# currently unacknowledged published messages. Returns immediately
|
1560
|
+
# if there are no outstanding confirms.
|
1359
1561
|
#
|
1360
|
-
# @return [Boolean] true if all messages were acknowledged positively, false otherwise
|
1562
|
+
# @return [Boolean] true if all messages were acknowledged positively since the last time this method was called, false otherwise
|
1361
1563
|
# @see #confirm_select
|
1362
1564
|
# @see #unconfirmed_set
|
1363
1565
|
# @see #nacked_set
|
1364
1566
|
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
|
1365
1567
|
# @api public
|
1366
1568
|
def wait_for_confirms
|
1367
|
-
@only_acks_received = true
|
1368
1569
|
wait_on_confirms_continuations
|
1369
|
-
|
1370
|
-
@only_acks_received
|
1570
|
+
read_and_reset_only_acks_received
|
1371
1571
|
end
|
1372
1572
|
|
1373
1573
|
# @endgroup
|
@@ -1386,7 +1586,8 @@ module Bunny
|
|
1386
1586
|
# @return [String] Unique string.
|
1387
1587
|
# @api plugin
|
1388
1588
|
def generate_consumer_tag(name = "bunny")
|
1389
|
-
|
1589
|
+
t = Bunny::Timestamp.now
|
1590
|
+
"#{name}-#{t.to_i * 1000}-#{Kernel.rand(999_999_999_999)}"
|
1390
1591
|
end
|
1391
1592
|
|
1392
1593
|
# @endgroup
|
@@ -1404,6 +1605,13 @@ module Bunny
|
|
1404
1605
|
@on_error = block
|
1405
1606
|
end
|
1406
1607
|
|
1608
|
+
# Defines a handler for uncaught exceptions in consumers
|
1609
|
+
# (e.g. delivered message handlers).
|
1610
|
+
#
|
1611
|
+
# @api public
|
1612
|
+
def on_uncaught_exception(&block)
|
1613
|
+
@uncaught_exception_handler = block
|
1614
|
+
end
|
1407
1615
|
|
1408
1616
|
#
|
1409
1617
|
# Recovery
|
@@ -1416,10 +1624,12 @@ module Bunny
|
|
1416
1624
|
#
|
1417
1625
|
# @api plugin
|
1418
1626
|
def recover_from_network_failure
|
1419
|
-
@logger.debug "Recovering channel #{@id} after network failure"
|
1627
|
+
@logger.debug { "Recovering channel #{@id} after network failure" }
|
1420
1628
|
release_all_continuations
|
1421
1629
|
|
1422
1630
|
recover_prefetch_setting
|
1631
|
+
recover_confirm_mode
|
1632
|
+
recover_tx_mode
|
1423
1633
|
recover_exchanges
|
1424
1634
|
# this includes recovering bindings
|
1425
1635
|
recover_queues
|
@@ -1432,7 +1642,30 @@ module Bunny
|
|
1432
1642
|
#
|
1433
1643
|
# @api plugin
|
1434
1644
|
def recover_prefetch_setting
|
1435
|
-
basic_qos(@prefetch_count) if @prefetch_count
|
1645
|
+
basic_qos(@prefetch_count, @prefetch_global) if @prefetch_count
|
1646
|
+
end
|
1647
|
+
|
1648
|
+
# Recovers publisher confirms mode. Used by the Automatic Network Failure
|
1649
|
+
# Recovery feature.
|
1650
|
+
# Set the offset to the previous publish sequence index as the protocol will reset the index to after recovery.
|
1651
|
+
#
|
1652
|
+
# @api plugin
|
1653
|
+
def recover_confirm_mode
|
1654
|
+
if using_publisher_confirmations?
|
1655
|
+
@unconfirmed_set_mutex.synchronize do
|
1656
|
+
@unconfirmed_set.clear
|
1657
|
+
@delivery_tag_offset = @next_publish_seq_no - 1
|
1658
|
+
end
|
1659
|
+
confirm_select(@confirms_callback)
|
1660
|
+
end
|
1661
|
+
end
|
1662
|
+
|
1663
|
+
# Recovers transaction mode. Used by the Automatic Network Failure
|
1664
|
+
# Recovery feature.
|
1665
|
+
#
|
1666
|
+
# @api plugin
|
1667
|
+
def recover_tx_mode
|
1668
|
+
tx_select if @tx_mode
|
1436
1669
|
end
|
1437
1670
|
|
1438
1671
|
# Recovers exchanges. Used by the Automatic Network Failure
|
@@ -1440,7 +1673,7 @@ module Bunny
|
|
1440
1673
|
#
|
1441
1674
|
# @api plugin
|
1442
1675
|
def recover_exchanges
|
1443
|
-
@exchanges.values.
|
1676
|
+
@exchange_mutex.synchronize { @exchanges.values }.each do |x|
|
1444
1677
|
x.recover_from_network_failure
|
1445
1678
|
end
|
1446
1679
|
end
|
@@ -1450,8 +1683,8 @@ module Bunny
|
|
1450
1683
|
#
|
1451
1684
|
# @api plugin
|
1452
1685
|
def recover_queues
|
1453
|
-
@queues.values.
|
1454
|
-
@logger.debug "Recovering queue #{q.name}"
|
1686
|
+
@queue_mutex.synchronize { @queues.values }.each do |q|
|
1687
|
+
@logger.debug { "Recovering queue #{q.name}" }
|
1455
1688
|
q.recover_from_network_failure
|
1456
1689
|
end
|
1457
1690
|
end
|
@@ -1462,10 +1695,11 @@ module Bunny
|
|
1462
1695
|
# @api plugin
|
1463
1696
|
def recover_consumers
|
1464
1697
|
unless @consumers.empty?
|
1465
|
-
@work_pool = ConsumerWorkPool.new(@work_pool.size)
|
1698
|
+
@work_pool = ConsumerWorkPool.new(@work_pool.size, @work_pool.abort_on_exception)
|
1466
1699
|
@work_pool.start
|
1467
1700
|
end
|
1468
|
-
|
1701
|
+
|
1702
|
+
@consumer_mutex.synchronize { @consumers.values }.each do |c|
|
1469
1703
|
c.recover_from_network_failure
|
1470
1704
|
end
|
1471
1705
|
end
|
@@ -1475,12 +1709,26 @@ module Bunny
|
|
1475
1709
|
@recoveries_counter.increment
|
1476
1710
|
end
|
1477
1711
|
|
1712
|
+
# @api public
|
1713
|
+
def recover_cancelled_consumers!
|
1714
|
+
@recover_cancelled_consumers = true
|
1715
|
+
end
|
1716
|
+
|
1717
|
+
# @api public
|
1718
|
+
def recovers_cancelled_consumers?
|
1719
|
+
!!@recover_cancelled_consumers
|
1720
|
+
end
|
1721
|
+
|
1478
1722
|
# @endgroup
|
1479
1723
|
|
1480
1724
|
|
1481
1725
|
# @return [String] Brief human-readable representation of the channel
|
1482
1726
|
def to_s
|
1483
|
-
"#<#{self.class.name}:#{object_id} @id=#{self.number} @connection=#{@connection.to_s}>"
|
1727
|
+
"#<#{self.class.name}:#{object_id} @id=#{self.number} @connection=#{@connection.to_s} @open=#{open?}>"
|
1728
|
+
end
|
1729
|
+
|
1730
|
+
def inspect
|
1731
|
+
to_s
|
1484
1732
|
end
|
1485
1733
|
|
1486
1734
|
|
@@ -1488,6 +1736,11 @@ module Bunny
|
|
1488
1736
|
# Implementation
|
1489
1737
|
#
|
1490
1738
|
|
1739
|
+
# @private
|
1740
|
+
def with_continuation_timeout(&block)
|
1741
|
+
Bunny::Timeout.timeout(wait_on_continuations_timeout, ClientTimeout, &block)
|
1742
|
+
end
|
1743
|
+
|
1491
1744
|
# @private
|
1492
1745
|
def register_consumer(consumer_tag, consumer)
|
1493
1746
|
@consumer_mutex.synchronize do
|
@@ -1511,12 +1764,33 @@ module Bunny
|
|
1511
1764
|
end
|
1512
1765
|
end
|
1513
1766
|
|
1767
|
+
# @private
|
1768
|
+
def pending_server_named_queue_declaration?
|
1769
|
+
@pending_queue_declare_name && @pending_queue_declare_name.empty?
|
1770
|
+
end
|
1771
|
+
|
1772
|
+
# @private
|
1773
|
+
def can_accept_queue_declare_ok?(method)
|
1774
|
+
@pending_queue_declare_name == method.queue ||
|
1775
|
+
pending_server_named_queue_declaration?
|
1776
|
+
end
|
1777
|
+
|
1514
1778
|
# @private
|
1515
1779
|
def handle_method(method)
|
1516
|
-
@logger.debug "Channel#handle_frame on channel #{@id}: #{method.inspect}"
|
1780
|
+
@logger.debug { "Channel#handle_frame on channel #{@id}: #{method.inspect}" }
|
1517
1781
|
case method
|
1518
1782
|
when AMQ::Protocol::Queue::DeclareOk then
|
1519
|
-
|
1783
|
+
# safeguard against late arrivals of responses and
|
1784
|
+
# so on, see ruby-amqp/bunny#558
|
1785
|
+
if can_accept_queue_declare_ok?(method)
|
1786
|
+
@continuations.push(method)
|
1787
|
+
else
|
1788
|
+
if !pending_server_named_queue_declaration?
|
1789
|
+
# this response is for an outdated/overwritten
|
1790
|
+
# queue.declare, drop it
|
1791
|
+
@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 concurrent channel use or a timeout, ignoring it"
|
1792
|
+
end
|
1793
|
+
end
|
1520
1794
|
when AMQ::Protocol::Queue::DeleteOk then
|
1521
1795
|
@continuations.push(method)
|
1522
1796
|
when AMQ::Protocol::Queue::PurgeOk then
|
@@ -1545,10 +1819,18 @@ module Bunny
|
|
1545
1819
|
if consumer = @consumers[method.consumer_tag]
|
1546
1820
|
@work_pool.submit do
|
1547
1821
|
begin
|
1548
|
-
|
1549
|
-
|
1822
|
+
if recovers_cancelled_consumers?
|
1823
|
+
consumer.handle_cancellation(method)
|
1824
|
+
@logger.info "Automatically recovering cancelled consumer #{consumer.consumer_tag} on queue #{consumer.queue_name}"
|
1825
|
+
|
1826
|
+
consume_with(consumer)
|
1827
|
+
else
|
1828
|
+
@consumers.delete(method.consumer_tag)
|
1829
|
+
consumer.handle_cancellation(method)
|
1830
|
+
end
|
1550
1831
|
rescue Exception => e
|
1551
1832
|
@logger.error "Got exception when notifying consumer #{method.consumer_tag} about cancellation!"
|
1833
|
+
@uncaught_exception_handler.call(e, consumer) if @uncaught_exception_handler
|
1552
1834
|
end
|
1553
1835
|
end
|
1554
1836
|
else
|
@@ -1588,7 +1870,7 @@ module Bunny
|
|
1588
1870
|
|
1589
1871
|
# @private
|
1590
1872
|
def channel_level_exception_after_operation_that_has_no_response?(method)
|
1591
|
-
method.reply_code == 406 && method.reply_text =~ /unknown delivery tag/
|
1873
|
+
method.reply_code == 406 && (method.reply_text =~ /unknown delivery tag/ || method.reply_text =~ /delivery acknowledgement on channel \d+ timed out/)
|
1592
1874
|
end
|
1593
1875
|
|
1594
1876
|
# @private
|
@@ -1607,7 +1889,11 @@ module Bunny
|
|
1607
1889
|
consumer = @consumers[basic_deliver.consumer_tag]
|
1608
1890
|
if consumer
|
1609
1891
|
@work_pool.submit do
|
1610
|
-
|
1892
|
+
begin
|
1893
|
+
consumer.call(DeliveryInfo.new(basic_deliver, consumer, self), MessageProperties.new(properties), content)
|
1894
|
+
rescue StandardError => e
|
1895
|
+
@uncaught_exception_handler.call(e, consumer) if @uncaught_exception_handler
|
1896
|
+
end
|
1611
1897
|
end
|
1612
1898
|
else
|
1613
1899
|
@logger.warn "No consumer for tag #{basic_deliver.consumer_tag} on channel #{@id}!"
|
@@ -1625,29 +1911,29 @@ module Bunny
|
|
1625
1911
|
end
|
1626
1912
|
end
|
1627
1913
|
|
1914
|
+
# Handle delivery tag offset calculations to keep the the delivery tag monotonic after a reset
|
1915
|
+
# due to automatic network failure recovery. @unconfirmed_set contains indices already offsetted.
|
1628
1916
|
# @private
|
1629
|
-
def handle_ack_or_nack(
|
1630
|
-
|
1631
|
-
|
1632
|
-
|
1633
|
-
|
1634
|
-
|
1635
|
-
|
1636
|
-
|
1917
|
+
def handle_ack_or_nack(delivery_tag_before_offset, multiple, nack)
|
1918
|
+
@unconfirmed_set_mutex.synchronize do
|
1919
|
+
delivery_tag = delivery_tag_before_offset + @delivery_tag_offset
|
1920
|
+
confirmed_range_start = multiple ? @unconfirmed_set.min : delivery_tag
|
1921
|
+
confirmed_range_end = delivery_tag
|
1922
|
+
confirmed_range = (confirmed_range_start..confirmed_range_end)
|
1923
|
+
|
1924
|
+
if nack
|
1925
|
+
@nacked_set.merge(@unconfirmed_set & confirmed_range)
|
1637
1926
|
end
|
1638
|
-
end
|
1639
1927
|
|
1640
|
-
|
1641
|
-
@unconfirmed_set.delete_if { |i| i <= delivery_tag }
|
1642
|
-
else
|
1643
|
-
@unconfirmed_set.delete(delivery_tag)
|
1644
|
-
end
|
1928
|
+
@unconfirmed_set.subtract(confirmed_range)
|
1645
1929
|
|
1646
|
-
@unconfirmed_set_mutex.synchronize do
|
1647
1930
|
@only_acks_received = (@only_acks_received && !nack)
|
1648
1931
|
|
1649
1932
|
@confirms_continuations.push(true) if @unconfirmed_set.empty?
|
1650
|
-
|
1933
|
+
|
1934
|
+
if @confirms_callback
|
1935
|
+
confirmed_range.each { |tag| @confirms_callback.call(tag, false, nack) }
|
1936
|
+
end
|
1651
1937
|
end
|
1652
1938
|
end
|
1653
1939
|
|
@@ -1689,22 +1975,37 @@ module Bunny
|
|
1689
1975
|
|
1690
1976
|
# @private
|
1691
1977
|
def wait_on_confirms_continuations
|
1978
|
+
raise_if_no_longer_open!
|
1979
|
+
|
1692
1980
|
if @connection.threaded
|
1693
1981
|
t = Thread.current
|
1694
1982
|
@threads_waiting_on_confirms_continuations << t
|
1695
1983
|
|
1696
1984
|
begin
|
1697
|
-
@
|
1985
|
+
while @unconfirmed_set_mutex.synchronize { !@unconfirmed_set.empty? }
|
1986
|
+
@confirms_continuations.poll(@connection.continuation_timeout)
|
1987
|
+
end
|
1698
1988
|
ensure
|
1699
1989
|
@threads_waiting_on_confirms_continuations.delete(t)
|
1700
1990
|
end
|
1701
1991
|
else
|
1702
|
-
|
1992
|
+
unless @unconfirmed_set.empty?
|
1993
|
+
connection.reader_loop.run_once until @confirms_continuations.length > 0
|
1994
|
+
@confirms_continuations.pop
|
1995
|
+
end
|
1996
|
+
end
|
1997
|
+
end
|
1703
1998
|
|
1704
|
-
|
1999
|
+
# @private
|
2000
|
+
def read_and_reset_only_acks_received
|
2001
|
+
@unconfirmed_set_mutex.synchronize do
|
2002
|
+
result = @only_acks_received
|
2003
|
+
@only_acks_received = true
|
2004
|
+
result
|
1705
2005
|
end
|
1706
2006
|
end
|
1707
2007
|
|
2008
|
+
|
1708
2009
|
# Releases all continuations. Used by automatic network recovery.
|
1709
2010
|
# @private
|
1710
2011
|
def release_all_continuations
|
@@ -1750,37 +2051,37 @@ module Bunny
|
|
1750
2051
|
|
1751
2052
|
# @private
|
1752
2053
|
def deregister_queue(queue)
|
1753
|
-
@queues.delete(queue.name)
|
2054
|
+
@queue_mutex.synchronize { @queues.delete(queue.name) }
|
1754
2055
|
end
|
1755
2056
|
|
1756
2057
|
# @private
|
1757
2058
|
def deregister_queue_named(name)
|
1758
|
-
@queues.delete(name)
|
2059
|
+
@queue_mutex.synchronize { @queues.delete(name) }
|
1759
2060
|
end
|
1760
2061
|
|
1761
2062
|
# @private
|
1762
2063
|
def register_queue(queue)
|
1763
|
-
@queues[queue.name] = queue
|
2064
|
+
@queue_mutex.synchronize { @queues[queue.name] = queue }
|
1764
2065
|
end
|
1765
2066
|
|
1766
2067
|
# @private
|
1767
2068
|
def find_queue(name)
|
1768
|
-
@queues[name]
|
2069
|
+
@queue_mutex.synchronize { @queues[name] }
|
1769
2070
|
end
|
1770
2071
|
|
1771
2072
|
# @private
|
1772
2073
|
def deregister_exchange(exchange)
|
1773
|
-
@exchanges.delete(exchange.name)
|
2074
|
+
@exchange_mutex.synchronize { @exchanges.delete(exchange.name) }
|
1774
2075
|
end
|
1775
2076
|
|
1776
2077
|
# @private
|
1777
2078
|
def register_exchange(exchange)
|
1778
|
-
@exchanges[exchange.name] = exchange
|
2079
|
+
@exchange_mutex.synchronize { @exchanges[exchange.name] = exchange }
|
1779
2080
|
end
|
1780
2081
|
|
1781
2082
|
# @private
|
1782
2083
|
def find_exchange(name)
|
1783
|
-
@exchanges[name]
|
2084
|
+
@exchange_mutex.synchronize { @exchanges[name] }
|
1784
2085
|
end
|
1785
2086
|
|
1786
2087
|
protected
|
@@ -1820,7 +2121,13 @@ module Bunny
|
|
1820
2121
|
|
1821
2122
|
# @private
|
1822
2123
|
def raise_if_no_longer_open!
|
1823
|
-
|
2124
|
+
if closed?
|
2125
|
+
if @last_channel_error
|
2126
|
+
raise ChannelAlreadyClosed.new("cannot use a closed channel! Channel id: #{@id}, closed due to a server-reported channel error: #{@last_channel_error.message}", self)
|
2127
|
+
else
|
2128
|
+
raise ChannelAlreadyClosed.new("cannot use a closed channel! Channel id: #{@id}", self)
|
2129
|
+
end
|
2130
|
+
end
|
1824
2131
|
end
|
1825
2132
|
|
1826
2133
|
# @private
|
@@ -1843,24 +2150,16 @@ module Bunny
|
|
1843
2150
|
@basic_get_continuations = new_continuation
|
1844
2151
|
end
|
1845
2152
|
|
1846
|
-
|
1847
|
-
|
1848
|
-
|
1849
|
-
|
1850
|
-
Concurrent::LinkedContinuationQueue.new
|
1851
|
-
end
|
1852
|
-
else
|
1853
|
-
# @private
|
1854
|
-
def new_continuation
|
1855
|
-
Concurrent::ContinuationQueue.new
|
1856
|
-
end
|
1857
|
-
end # if defined?
|
2153
|
+
# @private
|
2154
|
+
def new_continuation
|
2155
|
+
Concurrent::ContinuationQueue.new
|
2156
|
+
end
|
1858
2157
|
|
1859
2158
|
# @private
|
1860
2159
|
def guarding_against_stale_delivery_tags(tag, &block)
|
1861
2160
|
case tag
|
1862
2161
|
# if a fixnum was passed, execute unconditionally. MK.
|
1863
|
-
when
|
2162
|
+
when Integer then
|
1864
2163
|
block.call
|
1865
2164
|
# versioned delivery tags should be checked to avoid
|
1866
2165
|
# sending out stale (invalid) tags after channel was reopened
|