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.
Files changed (168) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +92 -87
  3. data/lib/amq/protocol/extensions.rb +2 -0
  4. data/lib/bunny/authentication/credentials_encoder.rb +2 -0
  5. data/lib/bunny/authentication/external_mechanism_encoder.rb +2 -0
  6. data/lib/bunny/authentication/plain_mechanism_encoder.rb +2 -0
  7. data/lib/bunny/channel.rb +485 -186
  8. data/lib/bunny/channel_id_allocator.rb +8 -4
  9. data/lib/bunny/concurrent/atomic_fixnum.rb +2 -0
  10. data/lib/bunny/concurrent/condition.rb +2 -0
  11. data/lib/bunny/concurrent/continuation_queue.rb +37 -13
  12. data/lib/bunny/concurrent/synchronized_sorted_set.rb +2 -0
  13. data/lib/bunny/consumer.rb +20 -13
  14. data/lib/bunny/consumer_tag_generator.rb +6 -2
  15. data/lib/bunny/consumer_work_pool.rb +37 -7
  16. data/lib/bunny/cruby/socket.rb +51 -22
  17. data/lib/bunny/cruby/ssl_socket.rb +68 -5
  18. data/lib/bunny/delivery_info.rb +3 -1
  19. data/lib/bunny/exceptions.rb +27 -4
  20. data/lib/bunny/exchange.rb +35 -29
  21. data/lib/bunny/framing.rb +2 -0
  22. data/lib/bunny/get_response.rb +85 -0
  23. data/lib/bunny/heartbeat_sender.rb +9 -6
  24. data/lib/bunny/message_properties.rb +2 -0
  25. data/lib/bunny/queue.rb +89 -41
  26. data/lib/bunny/reader_loop.rb +72 -28
  27. data/lib/bunny/return_info.rb +2 -0
  28. data/lib/bunny/session.rb +621 -225
  29. data/lib/bunny/socket.rb +7 -12
  30. data/lib/bunny/ssl_socket.rb +7 -12
  31. data/lib/bunny/test_kit.rb +15 -0
  32. data/lib/bunny/timeout.rb +3 -12
  33. data/lib/bunny/timestamp.rb +24 -0
  34. data/lib/bunny/transport.rb +223 -98
  35. data/lib/bunny/version.rb +2 -1
  36. data/lib/bunny/versioned_delivery_tag.rb +2 -0
  37. data/lib/bunny.rb +54 -8
  38. metadata +38 -224
  39. data/.gitignore +0 -22
  40. data/.rspec +0 -3
  41. data/.ruby-version +0 -1
  42. data/.travis.yml +0 -23
  43. data/.yardopts +0 -8
  44. data/ChangeLog.md +0 -1092
  45. data/Gemfile +0 -54
  46. data/LICENSE +0 -21
  47. data/benchmarks/basic_publish/with_128K_messages.rb +0 -35
  48. data/benchmarks/basic_publish/with_1k_messages.rb +0 -35
  49. data/benchmarks/basic_publish/with_4K_messages.rb +0 -35
  50. data/benchmarks/basic_publish/with_64K_messages.rb +0 -35
  51. data/benchmarks/channel_open.rb +0 -28
  52. data/benchmarks/mutex_and_monitor.rb +0 -42
  53. data/benchmarks/queue_declare.rb +0 -29
  54. data/benchmarks/queue_declare_and_bind.rb +0 -29
  55. data/benchmarks/queue_declare_bind_and_delete.rb +0 -29
  56. data/benchmarks/synchronized_sorted_set.rb +0 -53
  57. data/benchmarks/write_vs_write_nonblock.rb +0 -49
  58. data/bin/ci/before_build.sh +0 -31
  59. data/bunny.gemspec +0 -40
  60. data/examples/connection/authentication_failure.rb +0 -16
  61. data/examples/connection/automatic_recovery_with_basic_get.rb +0 -40
  62. data/examples/connection/automatic_recovery_with_client_named_queues.rb +0 -36
  63. data/examples/connection/automatic_recovery_with_multiple_consumers.rb +0 -46
  64. data/examples/connection/automatic_recovery_with_server_named_queues.rb +0 -35
  65. data/examples/connection/channel_level_exception.rb +0 -35
  66. data/examples/connection/disabled_automatic_recovery.rb +0 -34
  67. data/examples/connection/heartbeat.rb +0 -17
  68. data/examples/connection/manually_reconnecting_consumer.rb +0 -23
  69. data/examples/connection/manually_reconnecting_publisher.rb +0 -28
  70. data/examples/connection/unknown_host.rb +0 -16
  71. data/examples/guides/exchanges/direct_exchange_routing.rb +0 -36
  72. data/examples/guides/exchanges/fanout_exchange_routing.rb +0 -28
  73. data/examples/guides/exchanges/headers_exchange_routing.rb +0 -31
  74. data/examples/guides/exchanges/mandatory_messages.rb +0 -30
  75. data/examples/guides/extensions/alternate_exchange.rb +0 -28
  76. data/examples/guides/extensions/basic_nack.rb +0 -33
  77. data/examples/guides/extensions/connection_blocked.rb +0 -35
  78. data/examples/guides/extensions/consumer_cancellation_notification.rb +0 -39
  79. data/examples/guides/extensions/dead_letter_exchange.rb +0 -32
  80. data/examples/guides/extensions/exchange_to_exchange_bindings.rb +0 -29
  81. data/examples/guides/extensions/per_message_ttl.rb +0 -36
  82. data/examples/guides/extensions/per_queue_message_ttl.rb +0 -36
  83. data/examples/guides/extensions/publisher_confirms.rb +0 -28
  84. data/examples/guides/extensions/queue_lease.rb +0 -26
  85. data/examples/guides/extensions/sender_selected_distribution.rb +0 -32
  86. data/examples/guides/getting_started/blabbr.rb +0 -27
  87. data/examples/guides/getting_started/hello_world.rb +0 -20
  88. data/examples/guides/getting_started/weathr.rb +0 -47
  89. data/examples/guides/queues/one_off_consumer.rb +0 -23
  90. data/examples/guides/queues/redeliveries.rb +0 -79
  91. data/lib/bunny/compatibility.rb +0 -24
  92. data/lib/bunny/concurrent/linked_continuation_queue.rb +0 -61
  93. data/lib/bunny/jruby/socket.rb +0 -40
  94. data/lib/bunny/jruby/ssl_socket.rb +0 -53
  95. data/lib/bunny/system_timer.rb +0 -20
  96. data/profiling/basic_publish/with_4K_messages.rb +0 -33
  97. data/repl +0 -3
  98. data/spec/compatibility/queue_declare_spec.rb +0 -44
  99. data/spec/compatibility/queue_declare_with_default_channel_spec.rb +0 -33
  100. data/spec/higher_level_api/integration/basic_ack_spec.rb +0 -71
  101. data/spec/higher_level_api/integration/basic_cancel_spec.rb +0 -76
  102. data/spec/higher_level_api/integration/basic_consume_spec.rb +0 -225
  103. data/spec/higher_level_api/integration/basic_consume_with_objects_spec.rb +0 -54
  104. data/spec/higher_level_api/integration/basic_get_spec.rb +0 -48
  105. data/spec/higher_level_api/integration/basic_nack_spec.rb +0 -79
  106. data/spec/higher_level_api/integration/basic_publish_spec.rb +0 -89
  107. data/spec/higher_level_api/integration/basic_qos_spec.rb +0 -29
  108. data/spec/higher_level_api/integration/basic_recover_spec.rb +0 -18
  109. data/spec/higher_level_api/integration/basic_reject_spec.rb +0 -74
  110. data/spec/higher_level_api/integration/basic_return_spec.rb +0 -33
  111. data/spec/higher_level_api/integration/channel_close_spec.rb +0 -25
  112. data/spec/higher_level_api/integration/channel_flow_spec.rb +0 -21
  113. data/spec/higher_level_api/integration/channel_open_spec.rb +0 -57
  114. data/spec/higher_level_api/integration/confirm_select_spec.rb +0 -19
  115. data/spec/higher_level_api/integration/connection_spec.rb +0 -400
  116. data/spec/higher_level_api/integration/connection_stop_spec.rb +0 -26
  117. data/spec/higher_level_api/integration/consistent_hash_exchange_spec.rb +0 -50
  118. data/spec/higher_level_api/integration/consumer_cancellation_notification_spec.rb +0 -128
  119. data/spec/higher_level_api/integration/dead_lettering_spec.rb +0 -52
  120. data/spec/higher_level_api/integration/exchange_bind_spec.rb +0 -31
  121. data/spec/higher_level_api/integration/exchange_declare_spec.rb +0 -204
  122. data/spec/higher_level_api/integration/exchange_delete_spec.rb +0 -105
  123. data/spec/higher_level_api/integration/exchange_unbind_spec.rb +0 -40
  124. data/spec/higher_level_api/integration/exclusive_queue_spec.rb +0 -28
  125. data/spec/higher_level_api/integration/heartbeat_spec.rb +0 -31
  126. data/spec/higher_level_api/integration/merry_go_round_spec.rb +0 -85
  127. data/spec/higher_level_api/integration/message_properties_access_spec.rb +0 -95
  128. data/spec/higher_level_api/integration/predeclared_exchanges_spec.rb +0 -24
  129. data/spec/higher_level_api/integration/publisher_confirms_spec.rb +0 -77
  130. data/spec/higher_level_api/integration/publishing_edge_cases_spec.rb +0 -65
  131. data/spec/higher_level_api/integration/queue_bind_spec.rb +0 -109
  132. data/spec/higher_level_api/integration/queue_declare_spec.rb +0 -190
  133. data/spec/higher_level_api/integration/queue_delete_spec.rb +0 -41
  134. data/spec/higher_level_api/integration/queue_purge_spec.rb +0 -30
  135. data/spec/higher_level_api/integration/queue_unbind_spec.rb +0 -54
  136. data/spec/higher_level_api/integration/read_only_consumer_spec.rb +0 -60
  137. data/spec/higher_level_api/integration/sender_selected_distribution_spec.rb +0 -36
  138. data/spec/higher_level_api/integration/tls_connection_spec.rb +0 -127
  139. data/spec/higher_level_api/integration/tx_commit_spec.rb +0 -21
  140. data/spec/higher_level_api/integration/tx_rollback_spec.rb +0 -21
  141. data/spec/higher_level_api/integration/with_channel_spec.rb +0 -25
  142. data/spec/issues/issue100_spec.rb +0 -42
  143. data/spec/issues/issue141_spec.rb +0 -44
  144. data/spec/issues/issue78_spec.rb +0 -75
  145. data/spec/issues/issue83_spec.rb +0 -31
  146. data/spec/issues/issue97_attachment.json +0 -1
  147. data/spec/issues/issue97_spec.rb +0 -176
  148. data/spec/lower_level_api/integration/basic_cancel_spec.rb +0 -69
  149. data/spec/lower_level_api/integration/basic_consume_spec.rb +0 -100
  150. data/spec/spec_helper.rb +0 -64
  151. data/spec/stress/channel_open_stress_spec.rb +0 -51
  152. data/spec/stress/channel_open_stress_with_single_threaded_connection_spec.rb +0 -28
  153. data/spec/stress/concurrent_consumers_stress_spec.rb +0 -69
  154. data/spec/stress/concurrent_publishers_stress_spec.rb +0 -57
  155. data/spec/stress/connection_open_close_spec.rb +0 -40
  156. data/spec/stress/long_running_consumer_spec.rb +0 -83
  157. data/spec/tls/cacert.pem +0 -18
  158. data/spec/tls/client_cert.pem +0 -18
  159. data/spec/tls/client_key.pem +0 -27
  160. data/spec/tls/server_cert.pem +0 -18
  161. data/spec/tls/server_key.pem +0 -27
  162. data/spec/unit/bunny_spec.rb +0 -15
  163. data/spec/unit/concurrent/atomic_fixnum_spec.rb +0 -35
  164. data/spec/unit/concurrent/condition_spec.rb +0 -82
  165. data/spec/unit/concurrent/linked_continuation_queue_spec.rb +0 -35
  166. data/spec/unit/concurrent/synchronized_sorted_set_spec.rb +0 -73
  167. data/spec/unit/system_timer_spec.rb +0 -10
  168. data/spec/unit/version_delivery_tag_spec.rb +0 -28
@@ -1,4 +1,6 @@
1
- require "bunny/compatibility"
1
+ # frozen_string_literal: true
2
+
3
+ require 'amq/protocol'
2
4
 
3
5
  module Bunny
4
6
  # Represents AMQP 0.9.1 exchanges.
@@ -7,9 +9,6 @@ module Bunny
7
9
  # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
8
10
  class Exchange
9
11
 
10
- include Bunny::Compatibility
11
-
12
-
13
12
  #
14
13
  # API
15
14
  #
@@ -33,12 +32,12 @@ module Bunny
33
32
  attr_accessor :opts
34
33
 
35
34
 
36
- # The default exchange. Default exchange is a direct exchange that is predefined.
37
- # It cannot be removed. Every queue is bind to this (direct) exchange by default with
38
- # the following routing semantics: messages will be routed to the queue withe same
39
- # same name as message's routing key. In other words, if a message is published with
40
- # a routing key of "weather.usa.ca.sandiego" and there is a queue Q with this name,
41
- # that message will be routed to Q.
35
+ # The default exchange. This exchange is a direct exchange that is predefined by the broker
36
+ # and that cannot be removed. Every queue is bound to this exchange by default with
37
+ # the following routing semantics: messages will be routed to the queue with the same
38
+ # name as the message's routing key. In other words, if a message is published with
39
+ # a routing key of "weather.usa.ca.sandiego" and there is a queue with this name,
40
+ # the message will be routed to the queue.
42
41
  #
43
42
  # @param [Bunny::Channel] channel_or_connection Channel to use. {Bunny::Session} instances
44
43
  # are only supported for backwards compatibility.
@@ -46,23 +45,22 @@ module Bunny
46
45
  # @example Publishing a messages to the tasks queue
47
46
  # channel = Bunny::Channel.new(connection)
48
47
  # tasks_queue = channel.queue("tasks")
49
- # Bunny::Exchange.default(channel).publish("make clean", routing_key => "tasks")
48
+ # Bunny::Exchange.default(channel).publish("make clean", :routing_key => "tasks")
50
49
  #
51
50
  # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
52
51
  # @see http://www.rabbitmq.com/resources/specs/amqp0-9-1.pdf AMQP 0.9.1 specification (Section 2.1.2.4)
53
- # @note Do not confuse default exchange with amq.direct: amq.direct is a pre-defined direct
52
+ # @note Do not confuse the default exchange with amq.direct: amq.direct is a pre-defined direct
54
53
  # exchange that doesn't have any special routing semantics.
55
54
  # @return [Exchange] An instance that corresponds to the default exchange (of type direct).
56
55
  # @api public
57
56
  def self.default(channel_or_connection)
58
- self.new(channel_from(channel_or_connection), :direct, AMQ::Protocol::EMPTY_STRING, :no_declare => true)
57
+ self.new(channel_or_connection, :direct, AMQ::Protocol::EMPTY_STRING, :no_declare => true)
59
58
  end
60
59
 
61
- # @param [Bunny::Channel] channel_or_connection Channel this exchange will use. {Bunny::Session} instances are supported only for
62
- # backwards compatibility with 0.8.
63
- # @param [Symbol,String] type Exchange type
64
- # @param [String] name Exchange name
65
- # @param [Hash] opts Exchange properties
60
+ # @param [Bunny::Channel] channel Channel this exchange will use.
61
+ # @param [Symbol,String] type Exchange type
62
+ # @param [String] name Exchange name
63
+ # @param [Hash] opts Exchange properties
66
64
  #
67
65
  # @option opts [Boolean] :durable (false) Should this exchange be durable?
68
66
  # @option opts [Boolean] :auto_delete (false) Should this exchange be automatically deleted when it is no longer used?
@@ -75,18 +73,19 @@ module Bunny
75
73
  # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
76
74
  # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
77
75
  # @api public
78
- def initialize(channel_or_connection, type, name, opts = {})
79
- # old Bunny versions pass a connection here. In that case,
80
- # we just use default channel from it. MK.
81
- @channel = channel_from(channel_or_connection)
76
+ def initialize(channel, type, name, opts = {})
77
+ @channel = channel
82
78
  @name = name
83
79
  @type = type
84
80
  @options = self.class.add_default_options(name, opts)
85
81
 
86
82
  @durable = @options[:durable]
87
83
  @auto_delete = @options[:auto_delete]
84
+ @internal = @options[:internal]
88
85
  @arguments = @options[:arguments]
89
86
 
87
+ @bindings = Set.new
88
+
90
89
  declare! unless opts[:no_declare] || predeclared? || (@name == AMQ::Protocol::EMPTY_STRING)
91
90
 
92
91
  @channel.register_exchange(self)
@@ -104,6 +103,12 @@ module Bunny
104
103
  @auto_delete
105
104
  end # auto_delete?
106
105
 
106
+ # @return [Boolean] true if this exchange is internal (used solely for exchange-to-exchange
107
+ # bindings and cannot be published to by clients)
108
+ def internal?
109
+ @internal
110
+ end
111
+
107
112
  # @return [Hash] Additional optional arguments (typically used by RabbitMQ extensions and plugins)
108
113
  # @api public
109
114
  def arguments
@@ -170,6 +175,7 @@ module Bunny
170
175
  # @api public
171
176
  def bind(source, opts = {})
172
177
  @channel.exchange_bind(source, self, opts)
178
+ @bindings.add(source: source, opts: opts)
173
179
 
174
180
  self
175
181
  end
@@ -190,6 +196,7 @@ module Bunny
190
196
  # @api public
191
197
  def unbind(source, opts = {})
192
198
  @channel.exchange_unbind(source, self, opts)
199
+ @bindings.delete(source: source, opts: opts)
193
200
 
194
201
  self
195
202
  end
@@ -215,8 +222,11 @@ module Bunny
215
222
 
216
223
  # @private
217
224
  def recover_from_network_failure
218
- # puts "Recovering exchange #{@name} from network failure"
219
- declare! unless predefined?
225
+ declare! unless @options[:no_declare] ||predefined?
226
+
227
+ @bindings.each do |b|
228
+ bind(b[:source], b[:opts])
229
+ end
220
230
  end
221
231
 
222
232
 
@@ -246,11 +256,6 @@ module Bunny
246
256
  @channel.exchange_declare(@name, @type, @options)
247
257
  end
248
258
 
249
- # @private
250
- def self.add_default_options(name, opts, block)
251
- { :exchange => name, :nowait => (block.nil? && !name.empty?) }.merge(opts)
252
- end
253
-
254
259
  # @private
255
260
  def self.add_default_options(name, opts)
256
261
  # :nowait is always false for Bunny
@@ -261,6 +266,7 @@ module Bunny
261
266
  :passive => false,
262
267
  :durable => false,
263
268
  :auto_delete => false,
269
+ :internal => false,
264
270
  :arguments => nil
265
271
  }.merge(h)
266
272
  else
data/lib/bunny/framing.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bunny
2
4
  # @private
3
5
  module Framing
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bunny/versioned_delivery_tag"
4
+
5
+ module Bunny
6
+ # Wraps {AMQ::Protocol::Basic::GetOk} to
7
+ # provide access to the delivery properties as immutable hash as
8
+ # well as methods.
9
+ class GetResponse
10
+
11
+ #
12
+ # Behaviors
13
+ #
14
+
15
+ include Enumerable
16
+
17
+ #
18
+ # API
19
+ #
20
+
21
+ # @return [Bunny::Channel] Channel this basic.get-ok response is on
22
+ attr_reader :channel
23
+
24
+ # @private
25
+ def initialize(get_ok, channel)
26
+ @get_ok = get_ok
27
+ @hash = {
28
+ :delivery_tag => @get_ok.delivery_tag,
29
+ :redelivered => @get_ok.redelivered,
30
+ :exchange => @get_ok.exchange,
31
+ :routing_key => @get_ok.routing_key,
32
+ :channel => channel
33
+ }
34
+ @channel = channel
35
+ end
36
+
37
+ # Iterates over the delivery properties
38
+ # @see Enumerable#each
39
+ def each(*args, &block)
40
+ @hash.each(*args, &block)
41
+ end
42
+
43
+ # Accesses delivery properties by key
44
+ # @see Hash#[]
45
+ def [](k)
46
+ @hash[k]
47
+ end
48
+
49
+ # @return [Hash] Hash representation of this delivery info
50
+ def to_hash
51
+ @hash
52
+ end
53
+
54
+ # @private
55
+ def to_s
56
+ to_hash.to_s
57
+ end
58
+
59
+ # @private
60
+ def inspect
61
+ to_hash.inspect
62
+ end
63
+
64
+ # @return [String] Delivery identifier that is used to acknowledge, reject and nack deliveries
65
+ def delivery_tag
66
+ @get_ok.delivery_tag
67
+ end
68
+
69
+ # @return [Boolean] true if this delivery is a redelivery (the message was requeued at least once)
70
+ def redelivered
71
+ @get_ok.redelivered
72
+ end
73
+ alias redelivered? redelivered
74
+
75
+ # @return [String] Name of the exchange this message was published to
76
+ def exchange
77
+ @get_ok.exchange
78
+ end
79
+
80
+ # @return [String] Routing key this message was published with
81
+ def routing_key
82
+ @get_ok.routing_key
83
+ end
84
+ end
85
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "thread"
2
4
  require "amq/protocol/client"
3
5
  require "amq/protocol/frame"
@@ -17,7 +19,7 @@ module Bunny
17
19
  @logger = logger
18
20
  @mutex = Monitor.new
19
21
 
20
- @last_activity_time = Time.now
22
+ @last_activity_time = Bunny::Timestamp.monotonic
21
23
  end
22
24
 
23
25
  def start(period = 30)
@@ -29,6 +31,7 @@ module Bunny
29
31
  @interval = [(period / 2) - 1, 0.4].max
30
32
 
31
33
  @thread = Thread.new(&method(:run))
34
+ @thread.report_on_exception = false if @thread.respond_to?(:report_on_exception)
32
35
  end
33
36
  end
34
37
 
@@ -37,7 +40,7 @@ module Bunny
37
40
  end
38
41
 
39
42
  def signal_activity!
40
- @last_activity_time = Time.now
43
+ @last_activity_time = Bunny::Timestamp.monotonic
41
44
  end
42
45
 
43
46
  protected
@@ -52,18 +55,18 @@ module Bunny
52
55
  rescue IOError => ioe
53
56
  @logger.error "I/O error in the hearbeat sender: #{ioe.message}"
54
57
  stop
55
- rescue Exception => e
58
+ rescue ::Exception => e
56
59
  @logger.error "Error in the hearbeat sender: #{e.message}"
57
60
  stop
58
61
  end
59
62
  end
60
63
 
61
64
  def beat
62
- now = Time.now
65
+ now = Bunny::Timestamp.monotonic
63
66
 
64
67
  if now > (@last_activity_time + @interval)
65
- @logger.debug "Sending a heartbeat, last activity time: #{@last_activity_time}, interval (s): #{@interval}"
66
- @transport.write_without_timeout(AMQ::Protocol::HeartbeatFrame.encode)
68
+ @logger.debug { "Sending a heartbeat, last activity time: #{@last_activity_time}, interval (s): #{@interval}" }
69
+ @transport.write_without_timeout(AMQ::Protocol::HeartbeatFrame.encode, true)
67
70
  end
68
71
  end
69
72
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bunny
2
4
  # Wraps basic properties hash as returned by amq-protocol to
3
5
  # provide access to the delivery properties as immutable hash as
data/lib/bunny/queue.rb CHANGED
@@ -1,4 +1,6 @@
1
- require "bunny/compatibility"
1
+ # frozen_string_literal: true
2
+
3
+ require "bunny/get_response"
2
4
 
3
5
  module Bunny
4
6
  # Represents AMQP 0.9.1 queue.
@@ -7,13 +9,27 @@ module Bunny
7
9
  # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
8
10
  class Queue
9
11
 
10
- include Bunny::Compatibility
11
-
12
-
13
12
  #
14
13
  # API
15
14
  #
16
15
 
16
+ module Types
17
+ QUORUM = "quorum"
18
+ CLASSIC = "classic"
19
+ STREAM = "stream"
20
+
21
+ KNOWN = [CLASSIC, QUORUM, STREAM]
22
+
23
+ def self.known?(q_type)
24
+ KNOWN.include?(q_type)
25
+ end
26
+ end
27
+
28
+ module XArgs
29
+ MAX_LENGTH = "x-max-length",
30
+ QUEUE_TYPE = "x-queue-type"
31
+ end
32
+
17
33
  # @return [Bunny::Channel] Channel this queue uses
18
34
  attr_reader :channel
19
35
  # @return [String] Queue name
@@ -21,33 +37,42 @@ module Bunny
21
37
  # @return [Hash] Options this queue was created with
22
38
  attr_reader :options
23
39
 
24
- # @param [Bunny::Channel] channel_or_connection Channel this queue will use. {Bunny::Session} instances are supported only for
25
- # backwards compatibility with 0.8.
40
+ # @param [Bunny::Channel] channel Channel this queue will use.
26
41
  # @param [String] name Queue name. Pass an empty string to make RabbitMQ generate a unique one.
27
42
  # @param [Hash] opts Queue properties
28
43
  #
29
44
  # @option opts [Boolean] :durable (false) Should this queue be durable?
30
45
  # @option opts [Boolean] :auto_delete (false) Should this queue be automatically deleted when the last consumer disconnects?
31
46
  # @option opts [Boolean] :exclusive (false) Should this queue be exclusive (only can be used by this connection, removed when the connection is closed)?
32
- # @option opts [Boolean] :arguments ({}) Additional optional arguments (typically used by RabbitMQ extensions and plugins)
47
+ # @option opts [String] :type (nil) Type of the declared queue (classic, quorum or stream)
48
+ # @option opts [Hash] :arguments (nil) Additional optional arguments (typically used by RabbitMQ extensions and plugins)
33
49
  #
34
50
  # @see Bunny::Channel#queue
35
51
  # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
36
52
  # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
37
53
  # @api public
38
- def initialize(channel_or_connection, name = AMQ::Protocol::EMPTY_STRING, opts = {})
54
+ def initialize(channel, name = AMQ::Protocol::EMPTY_STRING, opts = {})
39
55
  # old Bunny versions pass a connection here. In that case,
40
56
  # we just use default channel from it. MK.
41
- @channel = channel_from(channel_or_connection)
57
+ @channel = channel
42
58
  @name = name
43
59
  @options = self.class.add_default_options(name, opts)
44
- @consumers = Hash.new
45
60
 
46
61
  @durable = @options[:durable]
47
62
  @exclusive = @options[:exclusive]
48
63
  @server_named = @name.empty?
49
64
  @auto_delete = @options[:auto_delete]
50
- @arguments = @options[:arguments]
65
+ @type = @options[:type]
66
+
67
+ @arguments = if @type and !@type.empty? then
68
+ (@options[:arguments] || {}).merge({XArgs::QUEUE_TYPE => @type})
69
+ else
70
+ @options[:arguments]
71
+ end
72
+ verify_type!(@arguments)
73
+ # reassigns updated and verified arguments because Bunny::Channel#declare_queue
74
+ # accepts a map of options
75
+ @options[:arguments] = @arguments
51
76
 
52
77
  @bindings = Array.new
53
78
 
@@ -92,6 +117,15 @@ module Bunny
92
117
  @arguments
93
118
  end
94
119
 
120
+ def to_s
121
+ oid = ("0x%x" % (self.object_id << 1))
122
+ "<#{self.class.name}:#{oid} @name=\"#{name}\" channel=#{@channel.to_s} @durable=#{@durable} @auto_delete=#{@auto_delete} @exclusive=#{@exclusive} @arguments=#{@arguments}>"
123
+ end
124
+
125
+ def inspect
126
+ to_s
127
+ end
128
+
95
129
  # Binds queue to an exchange
96
130
  #
97
131
  # @param [Bunny::Exchange,String] exchange Exchange to bind to
@@ -151,9 +185,9 @@ module Bunny
151
185
  #
152
186
  # @param [Hash] opts Options
153
187
  #
188
+ # @option opts [Boolean] :ack (false) [DEPRECATED] Use :manual_ack instead
154
189
  # @option opts [Boolean] :manual_ack (false) Will this consumer use manual acknowledgements?
155
190
  # @option opts [Boolean] :exclusive (false) Should this consumer be exclusive for this queue?
156
- # @option opts [Boolean] :block (false) Should the call block calling thread?
157
191
  # @option opts [#call] :on_cancellation Block to execute when this consumer is cancelled remotely (e.g. via the RabbitMQ Management plugin)
158
192
  # @option opts [String] :consumer_tag Unique consumer identifier. It is usually recommended to let Bunny generate it for you.
159
193
  # @option opts [Hash] :arguments ({}) Additional (optional) arguments, typically used by RabbitMQ extensions
@@ -162,17 +196,22 @@ module Bunny
162
196
  # @api public
163
197
  def subscribe(opts = {
164
198
  :consumer_tag => @channel.generate_consumer_tag,
165
- :ack => false,
199
+ :manual_ack => false,
166
200
  :exclusive => false,
167
201
  :block => false,
168
202
  :on_cancellation => nil
169
203
  }, &block)
170
204
 
205
+ unless opts[:ack].nil?
206
+ warn "[DEPRECATION] `:ack` is deprecated. Please use `:manual_ack` instead."
207
+ opts[:manual_ack] = opts[:ack]
208
+ end
209
+
171
210
  ctag = opts.fetch(:consumer_tag, @channel.generate_consumer_tag)
172
211
  consumer = Consumer.new(@channel,
173
212
  self,
174
213
  ctag,
175
- !(opts[:ack] || opts[:manual_ack]),
214
+ !opts[:manual_ack],
176
215
  opts[:exclusive],
177
216
  opts[:arguments])
178
217
 
@@ -207,7 +246,8 @@ module Bunny
207
246
 
208
247
  # @param [Hash] opts Options
209
248
  #
210
- # @option opts [Boolean] :ack (false) Will the message be acknowledged manually?
249
+ # @option opts [Boolean] :ack (false) [DEPRECATED] Use :manual_ack instead
250
+ # @option opts [Boolean] :manual_ack (false) Will the message be acknowledged manually?
211
251
  #
212
252
  # @return [Array] Triple of delivery info, message properties and message content.
213
253
  # If the queue is empty, all three will be nils.
@@ -228,33 +268,34 @@ module Bunny
228
268
  #
229
269
  # puts "This is the message: " + payload + "\n\n"
230
270
  # conn.close
231
- def pop(opts = {:ack => false}, &block)
232
- delivery_info, properties, content = @channel.basic_get(@name, opts)
233
-
234
- if block
235
- block.call(delivery_info, properties, content)
236
- else
237
- [delivery_info, properties, content]
271
+ def pop(opts = {:manual_ack => false}, &block)
272
+ unless opts[:ack].nil?
273
+ warn "[DEPRECATION] `:ack` is deprecated. Please use `:manual_ack` instead."
274
+ opts[:manual_ack] = opts[:ack]
238
275
  end
239
- end
240
- alias get pop
241
276
 
242
- # Version of {Bunny::Queue#pop} that returns data in legacy format
243
- # (as a hash).
244
- # @return [Hash]
245
- # @deprecated
246
- def pop_as_hash(opts = {:ack => false}, &block)
247
- delivery_info, properties, content = @channel.basic_get(@name, opts)
248
-
249
- result = {:header => properties, :payload => content, :delivery_details => delivery_info}
277
+ get_response, properties, content = @channel.basic_get(@name, opts)
250
278
 
251
279
  if block
252
- block.call(result)
280
+ if properties
281
+ di = GetResponse.new(get_response, @channel)
282
+ mp = MessageProperties.new(properties)
283
+
284
+ block.call(di, mp, content)
285
+ else
286
+ block.call(nil, nil, nil)
287
+ end
253
288
  else
254
- result
289
+ if properties
290
+ di = GetResponse.new(get_response, @channel)
291
+ mp = MessageProperties.new(properties)
292
+ [di, mp, content]
293
+ else
294
+ [nil, nil, nil]
295
+ end
255
296
  end
256
297
  end
257
-
298
+ alias get pop
258
299
 
259
300
  # Publishes a message to the queue via default exchange. Takes the same arguments
260
301
  # as {Bunny::Exchange#publish}
@@ -313,6 +354,14 @@ module Bunny
313
354
  s[:consumer_count]
314
355
  end
315
356
 
357
+ def self.verify_type!(args0 = {})
358
+ # be extra defensive
359
+ args = args0 || {}
360
+ q_type = args["x-queue-type"] || args[:"x-queue-type"]
361
+ throw ArgumentError.new(
362
+ "unsupported queue type #{q_type.inspect}, supported ones: #{Types::KNOWN.join(', ')}") if (q_type and !Types.known?(q_type))
363
+ end
364
+
316
365
  #
317
366
  # Recovery
318
367
  #
@@ -329,7 +378,7 @@ module Bunny
329
378
  # TODO: inject and use logger
330
379
  # puts "Recovering queue #{@name}"
331
380
  begin
332
- declare!
381
+ declare! unless @options[:no_declare]
333
382
 
334
383
  @channel.register_queue(self)
335
384
  rescue Exception => e
@@ -361,11 +410,6 @@ module Bunny
361
410
 
362
411
  protected
363
412
 
364
- # @private
365
- def self.add_default_options(name, opts, block)
366
- { :queue => name, :nowait => (block.nil? && !name.empty?) }.merge(opts)
367
- end
368
-
369
413
  # @private
370
414
  def self.add_default_options(name, opts)
371
415
  # :nowait is always false for Bunny
@@ -383,5 +427,9 @@ module Bunny
383
427
  h
384
428
  end
385
429
  end
430
+
431
+ def verify_type!(args)
432
+ self.class.verify_type!(args)
433
+ end
386
434
  end
387
435
  end