bunny 2.14.2 → 2.22.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (155) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +56 -39
  3. data/lib/bunny/channel.rb +89 -13
  4. data/lib/bunny/consumer.rb +2 -2
  5. data/lib/bunny/consumer_work_pool.rb +1 -1
  6. data/lib/bunny/cruby/socket.rb +3 -0
  7. data/lib/bunny/delivery_info.rb +1 -1
  8. data/lib/bunny/queue.rb +36 -5
  9. data/lib/bunny/reader_loop.rb +21 -13
  10. data/lib/bunny/session.rb +83 -25
  11. data/lib/bunny/transport.rb +49 -12
  12. data/lib/bunny/version.rb +1 -1
  13. data/lib/bunny.rb +45 -4
  14. metadata +37 -235
  15. data/.github/ISSUE_TEMPLATE.md +0 -18
  16. data/.gitignore +0 -28
  17. data/.rspec +0 -1
  18. data/.travis.yml +0 -31
  19. data/.yardopts +0 -8
  20. data/CONTRIBUTING.md +0 -132
  21. data/ChangeLog.md +0 -2072
  22. data/Gemfile +0 -55
  23. data/LICENSE +0 -21
  24. data/Rakefile +0 -54
  25. data/benchmarks/basic_publish/with_128K_messages.rb +0 -35
  26. data/benchmarks/basic_publish/with_1k_messages.rb +0 -35
  27. data/benchmarks/basic_publish/with_4K_messages.rb +0 -35
  28. data/benchmarks/basic_publish/with_64K_messages.rb +0 -35
  29. data/benchmarks/channel_open.rb +0 -28
  30. data/benchmarks/mutex_and_monitor.rb +0 -42
  31. data/benchmarks/queue_declare.rb +0 -29
  32. data/benchmarks/queue_declare_and_bind.rb +0 -29
  33. data/benchmarks/queue_declare_bind_and_delete.rb +0 -29
  34. data/benchmarks/synchronized_sorted_set.rb +0 -53
  35. data/benchmarks/write_vs_write_nonblock.rb +0 -49
  36. data/bunny.gemspec +0 -34
  37. data/docker/Dockerfile +0 -20
  38. data/docker/apt/preferences.d/erlang +0 -3
  39. data/docker/apt/sources.list.d/bintray.rabbitmq.list +0 -2
  40. data/docker/docker-entrypoint.sh +0 -26
  41. data/docker/rabbitmq.conf +0 -29
  42. data/docker-compose.yml +0 -28
  43. data/examples/connection/authentication_failure.rb +0 -16
  44. data/examples/connection/automatic_recovery_with_basic_get.rb +0 -40
  45. data/examples/connection/automatic_recovery_with_client_named_queues.rb +0 -36
  46. data/examples/connection/automatic_recovery_with_multiple_consumers.rb +0 -46
  47. data/examples/connection/automatic_recovery_with_republishing.rb +0 -109
  48. data/examples/connection/automatic_recovery_with_server_named_queues.rb +0 -35
  49. data/examples/connection/channel_level_exception.rb +0 -27
  50. data/examples/connection/disabled_automatic_recovery.rb +0 -34
  51. data/examples/connection/heartbeat.rb +0 -17
  52. data/examples/connection/manually_reconnecting_consumer.rb +0 -23
  53. data/examples/connection/manually_reconnecting_publisher.rb +0 -28
  54. data/examples/connection/unknown_host.rb +0 -16
  55. data/examples/consumers/high_and_low_priority.rb +0 -50
  56. data/examples/guides/exchanges/direct_exchange_routing.rb +0 -36
  57. data/examples/guides/exchanges/fanout_exchange_routing.rb +0 -28
  58. data/examples/guides/exchanges/headers_exchange_routing.rb +0 -31
  59. data/examples/guides/exchanges/mandatory_messages.rb +0 -30
  60. data/examples/guides/extensions/alternate_exchange.rb +0 -30
  61. data/examples/guides/extensions/basic_nack.rb +0 -33
  62. data/examples/guides/extensions/connection_blocked.rb +0 -35
  63. data/examples/guides/extensions/consumer_cancellation_notification.rb +0 -39
  64. data/examples/guides/extensions/dead_letter_exchange.rb +0 -32
  65. data/examples/guides/extensions/exchange_to_exchange_bindings.rb +0 -29
  66. data/examples/guides/extensions/per_message_ttl.rb +0 -36
  67. data/examples/guides/extensions/per_queue_message_ttl.rb +0 -36
  68. data/examples/guides/extensions/publisher_confirms.rb +0 -28
  69. data/examples/guides/extensions/queue_lease.rb +0 -26
  70. data/examples/guides/extensions/sender_selected_distribution.rb +0 -32
  71. data/examples/guides/getting_started/blabbr.rb +0 -27
  72. data/examples/guides/getting_started/hello_world.rb +0 -22
  73. data/examples/guides/getting_started/weathr.rb +0 -49
  74. data/examples/guides/queues/one_off_consumer.rb +0 -25
  75. data/examples/guides/queues/redeliveries.rb +0 -81
  76. data/profiling/basic_publish/with_4K_messages.rb +0 -33
  77. data/repl +0 -3
  78. data/spec/config/enabled_plugins +0 -1
  79. data/spec/config/rabbitmq.conf +0 -13
  80. data/spec/higher_level_api/integration/basic_ack_spec.rb +0 -230
  81. data/spec/higher_level_api/integration/basic_cancel_spec.rb +0 -142
  82. data/spec/higher_level_api/integration/basic_consume_spec.rb +0 -349
  83. data/spec/higher_level_api/integration/basic_consume_with_objects_spec.rb +0 -54
  84. data/spec/higher_level_api/integration/basic_get_spec.rb +0 -80
  85. data/spec/higher_level_api/integration/basic_nack_spec.rb +0 -82
  86. data/spec/higher_level_api/integration/basic_publish_spec.rb +0 -74
  87. data/spec/higher_level_api/integration/basic_qos_spec.rb +0 -57
  88. data/spec/higher_level_api/integration/basic_reject_spec.rb +0 -152
  89. data/spec/higher_level_api/integration/basic_return_spec.rb +0 -33
  90. data/spec/higher_level_api/integration/channel_close_spec.rb +0 -66
  91. data/spec/higher_level_api/integration/channel_open_spec.rb +0 -57
  92. data/spec/higher_level_api/integration/connection_recovery_spec.rb +0 -483
  93. data/spec/higher_level_api/integration/connection_spec.rb +0 -563
  94. data/spec/higher_level_api/integration/connection_stop_spec.rb +0 -83
  95. data/spec/higher_level_api/integration/consumer_cancellation_notification_spec.rb +0 -128
  96. data/spec/higher_level_api/integration/dead_lettering_spec.rb +0 -75
  97. data/spec/higher_level_api/integration/exchange_bind_spec.rb +0 -31
  98. data/spec/higher_level_api/integration/exchange_declare_spec.rb +0 -237
  99. data/spec/higher_level_api/integration/exchange_delete_spec.rb +0 -105
  100. data/spec/higher_level_api/integration/exchange_unbind_spec.rb +0 -40
  101. data/spec/higher_level_api/integration/exclusive_queue_spec.rb +0 -28
  102. data/spec/higher_level_api/integration/heartbeat_spec.rb +0 -49
  103. data/spec/higher_level_api/integration/message_properties_access_spec.rb +0 -95
  104. data/spec/higher_level_api/integration/predeclared_exchanges_spec.rb +0 -24
  105. data/spec/higher_level_api/integration/publisher_confirms_spec.rb +0 -191
  106. data/spec/higher_level_api/integration/publishing_edge_cases_spec.rb +0 -87
  107. data/spec/higher_level_api/integration/queue_bind_spec.rb +0 -109
  108. data/spec/higher_level_api/integration/queue_declare_spec.rb +0 -285
  109. data/spec/higher_level_api/integration/queue_delete_spec.rb +0 -41
  110. data/spec/higher_level_api/integration/queue_purge_spec.rb +0 -30
  111. data/spec/higher_level_api/integration/queue_unbind_spec.rb +0 -54
  112. data/spec/higher_level_api/integration/read_only_consumer_spec.rb +0 -60
  113. data/spec/higher_level_api/integration/sender_selected_distribution_spec.rb +0 -36
  114. data/spec/higher_level_api/integration/tls_connection_spec.rb +0 -250
  115. data/spec/higher_level_api/integration/toxiproxy_spec.rb +0 -76
  116. data/spec/higher_level_api/integration/tx_commit_spec.rb +0 -21
  117. data/spec/higher_level_api/integration/tx_rollback_spec.rb +0 -21
  118. data/spec/higher_level_api/integration/with_channel_spec.rb +0 -25
  119. data/spec/issues/issue100_spec.rb +0 -42
  120. data/spec/issues/issue141_spec.rb +0 -43
  121. data/spec/issues/issue202_spec.rb +0 -15
  122. data/spec/issues/issue224_spec.rb +0 -40
  123. data/spec/issues/issue465_spec.rb +0 -32
  124. data/spec/issues/issue549_spec.rb +0 -30
  125. data/spec/issues/issue78_spec.rb +0 -72
  126. data/spec/issues/issue83_spec.rb +0 -30
  127. data/spec/issues/issue97_attachment.json +0 -1
  128. data/spec/issues/issue97_spec.rb +0 -175
  129. data/spec/lower_level_api/integration/basic_cancel_spec.rb +0 -83
  130. data/spec/lower_level_api/integration/basic_consume_spec.rb +0 -99
  131. data/spec/spec_helper.rb +0 -47
  132. data/spec/stress/channel_close_stress_spec.rb +0 -64
  133. data/spec/stress/channel_open_stress_spec.rb +0 -84
  134. data/spec/stress/channel_open_stress_with_single_threaded_connection_spec.rb +0 -28
  135. data/spec/stress/concurrent_consumers_stress_spec.rb +0 -71
  136. data/spec/stress/concurrent_publishers_stress_spec.rb +0 -54
  137. data/spec/stress/connection_open_close_spec.rb +0 -52
  138. data/spec/stress/merry_go_round_spec.rb +0 -105
  139. data/spec/tls/ca_certificate.pem +0 -29
  140. data/spec/tls/ca_key.pem +0 -52
  141. data/spec/tls/client_certificate.pem +0 -29
  142. data/spec/tls/client_key.pem +0 -51
  143. data/spec/tls/generate-server-cert.sh +0 -8
  144. data/spec/tls/server-openssl.cnf +0 -10
  145. data/spec/tls/server.csr +0 -16
  146. data/spec/tls/server_certificate.pem +0 -29
  147. data/spec/tls/server_key.pem +0 -51
  148. data/spec/toxiproxy_helper.rb +0 -28
  149. data/spec/unit/bunny_spec.rb +0 -15
  150. data/spec/unit/concurrent/atomic_fixnum_spec.rb +0 -35
  151. data/spec/unit/concurrent/condition_spec.rb +0 -82
  152. data/spec/unit/concurrent/linked_continuation_queue_spec.rb +0 -35
  153. data/spec/unit/concurrent/synchronized_sorted_set_spec.rb +0 -73
  154. data/spec/unit/exchange_recovery_spec.rb +0 -39
  155. data/spec/unit/version_delivery_tag_spec.rb +0 -28
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7a6ffb6e1661c9921afd766382444c1d194f5b816d35021f9f49ab4382adced6
4
- data.tar.gz: 4f7ef1377379a4fa087110316c149c0f012239247537467bfd788463d8c0a904
3
+ metadata.gz: 2edd88411f92dbdbb1d06b334006e446d5b3e3fb165f3baae3745bd066cf9651
4
+ data.tar.gz: 437032f606bb6f77fee02e65706e615f8a52c0e05467bbe257f1796fcedd624c
5
5
  SHA512:
6
- metadata.gz: 73ada01a49fb86a5ffe95548b79df15169cd63ba47481c2303f5ea314f04e14e97c4f9e7e42b095969bf5259633d15803a4eb116743c054007d4694af62b6fa2
7
- data.tar.gz: b6c533137ea821b45c2dd7e8cc54a87c09384a6e15bad8ae505c3c9ea2ef3c3ed8a53153832bc1a0773adc566888a832e689837abfe8f5f4d1a90e7cf2966da6
6
+ metadata.gz: 698398dc76ca4e3dd11e3c5dc6b078998f23895bd3d975b01e1329c08c8ff0835cb9691d0108f0f6e13aaa769b870b258ff5e1de1958321be4804ca99b30a0be
7
+ data.tar.gz: 2a1fdcc61d683a991d07f601a986c5bc04d40cdd7155a1833dc7dba46be014c23d36612c505531b2f2adfe08ed057d6a493cbf5df368318205ffaf88659a88fb
data/README.md CHANGED
@@ -7,7 +7,7 @@ have any heavyweight dependencies.
7
7
 
8
8
  ## I Know What RabbitMQ and Bunny are, How Do I Get Started?
9
9
 
10
- [Right here](http://rubybunny.info/articles/getting_started.html)!
10
+ [Right here](https://www.rabbitmq.com/getstarted.html)!
11
11
 
12
12
 
13
13
  ## What is Bunny Good For?
@@ -43,12 +43,17 @@ Specific examples:
43
43
  Web applications that display that information in the real time.
44
44
 
45
45
 
46
-
47
46
  ## Supported Ruby Versions
48
47
 
49
48
  Modern Bunny versions support
50
49
 
51
- * CRuby 2.2 through 2.5 (inclusive)
50
+ * CRuby 2.6 through 3.1 (inclusive)
51
+ * [TruffleRuby](https://www.graalvm.org/ruby/)
52
+
53
+ For environments that use TLS, Bunny expects Ruby installations to use a recent enough OpenSSL version that
54
+ **includes support for TLS 1.3**.
55
+
56
+ ### JRuby
52
57
 
53
58
  Bunny works sufficiently well on JRuby but there are known
54
59
  JRuby bugs in versions prior to JRuby 9000 that cause high CPU burn. JRuby users should
@@ -59,8 +64,7 @@ Bunny `1.7.x` was the last version to support CRuby 1.9.3 and 1.8.7
59
64
 
60
65
  ## Supported RabbitMQ Versions
61
66
 
62
- Bunny `1.5.0` and later versions only support RabbitMQ `3.3+`.
63
- Bunny `1.4.x` and earlier supports RabbitMQ 2.x and 3.x.
67
+ Modern Bunny releases target [currently supported RabbitMQ release series](https://www.rabbitmq.com/versions.html).
64
68
 
65
69
 
66
70
  ## Change Log
@@ -70,11 +74,8 @@ a stable public API.
70
74
 
71
75
  Change logs per release series:
72
76
 
73
- * [master](https://github.com/ruby-amqp/bunny/blob/master/ChangeLog.md)
74
- * [2.13.x](https://github.com/ruby-amqp/bunny/blob/2.13.x-stable/ChangeLog.md)
75
- * [2.12.x](https://github.com/ruby-amqp/bunny/blob/2.12.x-stable/ChangeLog.md)
76
- * [2.11.x](https://github.com/ruby-amqp/bunny/blob/2.11.x-stable/ChangeLog.md)
77
- * [2.10.x](https://github.com/ruby-amqp/bunny/blob/2.10.x-stable/ChangeLog.md)
77
+ * [main](https://github.com/ruby-amqp/bunny/blob/main/ChangeLog.md) (most notable changes for all release series)
78
+ * [2.19.x](https://github.com/ruby-amqp/bunny/blob/2.19.x-stable/ChangeLog.md)
78
79
 
79
80
 
80
81
 
@@ -84,20 +85,20 @@ Change logs per release series:
84
85
 
85
86
  [![Gem Version](https://badge.fury.io/rb/bunny.svg)](http://badge.fury.io/rb/bunny)
86
87
 
87
- ### With Rubygems
88
+ ### Bundler Dependency
88
89
 
89
- To install Bunny with RubyGems:
90
+ To use Bunny in a project managed with Bundler:
90
91
 
91
- ```
92
- gem install bunny
92
+ ``` ruby
93
+ gem "bunny", ">= 2.19.0"
93
94
  ```
94
95
 
95
- ### Bundler Dependency
96
+ ### With Rubygems
96
97
 
97
- To use Bunny in a project managed with Bundler:
98
+ To install Bunny with RubyGems:
98
99
 
99
- ``` ruby
100
- gem "bunny", ">= 2.13.0"
100
+ ```
101
+ gem install bunny
101
102
  ```
102
103
 
103
104
 
@@ -106,7 +107,7 @@ gem "bunny", ">= 2.13.0"
106
107
  Below is a small snippet that demonstrates how to publish
107
108
  and synchronously consume ("pull API") messages with Bunny.
108
109
 
109
- For a 15 minute tutorial using more practical examples, see [Getting Started with RabbitMQ and Ruby using Bunny](http://rubybunny.info/articles/getting_started.html).
110
+ For a 15 minute tutorial using more practical examples, see [Getting Started with RabbitMQ and Ruby using Bunny](https://www.rabbitmq.com/tutorials/tutorial-one-ruby.html).
110
111
 
111
112
  ``` ruby
112
113
  require "bunny"
@@ -117,20 +118,31 @@ conn.start
117
118
 
118
119
  # open a channel
119
120
  ch = conn.create_channel
121
+ ch.confirm_select
120
122
 
121
123
  # declare a queue
122
124
  q = ch.queue("test1")
125
+ q.subscribe(manual_ack: true) do |delivery_info, metadata, payload|
126
+ puts "This is the message: #{payload}"
127
+ # acknowledge the delivery so that RabbitMQ can mark it for deletion
128
+ ch.ack(delivery_info.delivery_tag)
129
+ end
123
130
 
124
131
  # publish a message to the default exchange which then gets routed to this queue
125
132
  q.publish("Hello, everybody!")
126
133
 
127
- # fetch a message from the queue
128
- delivery_info, metadata, payload = q.pop
134
+ # await confirmations from RabbitMQ, see
135
+ # https://www.rabbitmq.com/publishers.html#data-safety for details
136
+ ch.wait_for_confirms
129
137
 
130
- puts "This is the message: #{payload}"
138
+ # give the above consumer some time consume the delivery and print out the message
139
+ sleep 1
131
140
 
141
+ puts "Done"
142
+
143
+ ch.close
132
144
  # close the connection
133
- conn.stop
145
+ conn.close
134
146
  ```
135
147
 
136
148
 
@@ -142,17 +154,29 @@ For a 15 minute tutorial using more practical examples, see [Getting Started wit
142
154
 
143
155
  ### Guides
144
156
 
145
- Other documentation guides are available at [rubybunny.info](http://rubybunny.info):
157
+ Bunny documentation guides are [under `docs/guides` in this repository](https://github.com/ruby-amqp/bunny/tree/main/docs/guides):
146
158
 
147
- * [Queues and Consumers](http://rubybunny.info/articles/queues.html)
148
- * [Exchanges and Publishers](http://rubybunny.info/articles/exchanges.html)
159
+ * [Queues and Consumers](https://github.com/ruby-amqp/bunny/tree/main/docs/guides/queues.md)
160
+ * [Exchanges and Publishers](https://github.com/ruby-amqp/bunny/tree/main/docs/guides/exchanges.md)
149
161
  * [AMQP 0.9.1 Model Explained](http://www.rabbitmq.com/tutorials/amqp-concepts.html)
150
- * [Connecting to RabbitMQ](http://rubybunny.info/articles/connecting.html)
151
- * [Error Handling and Recovery](http://rubybunny.info/articles/error_handling.html)
152
- * [TLS/SSL Support](http://rubybunny.info/articles/tls.html)
153
- * [Bindings](http://rubybunny.info/articles/bindings.html)
154
- * [Using RabbitMQ Extensions with Bunny](http://rubybunny.info/articles/extensions.html)
155
- * [Durability and Related Matters](http://rubybunny.info/articles/durability.html)
162
+ * [Connecting to RabbitMQ](https://github.com/ruby-amqp/bunny/tree/main/docs/guides/connecting.md)
163
+ * [Error Handling and Recovery](https://github.com/ruby-amqp/bunny/tree/main/docs/guides/error_handling.md)
164
+ * [TLS/SSL Support](https://github.com/ruby-amqp/bunny/tree/main/docs/guides/tls.md)
165
+ * [Bindings](https://github.com/ruby-amqp/bunny/tree/main/docs/guides/bindings.md)
166
+ * [Using RabbitMQ Extensions with Bunny](https://github.com/ruby-amqp/bunny/tree/main/docs/guides/extensions.md)
167
+ * [Durability and Related Matters](https://github.com/ruby-amqp/bunny/tree/main/docs/guides/durability.md)
168
+
169
+ Some highly relevant RabbitMQ documentation guides:
170
+
171
+ * [Connections](https://www.rabbitmq.com/connections.html)
172
+ * [Channels](https://www.rabbitmq.com/channels.html)
173
+ * [Queues](https://www.rabbitmq.com/queues.html)
174
+ * [Quorum queues](https://www.rabbitmq.com/quorum-queues.html)
175
+ * [Streams](https://rabbitmq.com/streams.html) (Bunny can perform basic operations on streams even though it does not implement the [RabbitMQ Stream protocol](https://github.com/rabbitmq/rabbitmq-server/blob/v3.10.x/deps/rabbitmq_stream/docs/PROTOCOL.adoc))
176
+ * [Publishers](https://www.rabbitmq.com/publishers.html)
177
+ * [Consumers](https://www.rabbitmq.com/consumers.html)
178
+ * Data safety: publisher and consumer [Confirmations](https://www.rabbitmq.com/confirms.html)
179
+ * [Production Checklist](https://www.rabbitmq.com/production-checklist.html)
156
180
 
157
181
  ### API Reference
158
182
 
@@ -176,13 +200,6 @@ mailing list. Feel free to ask any questions that you may have.
176
200
  [![Build Status](https://travis-ci.org/ruby-amqp/bunny.svg)](https://travis-ci.org/ruby-amqp/bunny/)
177
201
 
178
202
 
179
- ### News & Announcements on Twitter
180
-
181
- To subscribe for announcements of releases, important changes and so on, please follow [@rubyamqp](https://twitter.com/#!/rubyamqp) on Twitter.
182
-
183
- More detailed announcements can be found in the [RabbitMQ Ruby clients blog](http://blog.rubyrabbitmq.info).
184
-
185
-
186
203
  ### Reporting Issues
187
204
 
188
205
  If you find a bug you understand well, poor default, incorrect or unclear piece of documentation,
data/lib/bunny/channel.rb CHANGED
@@ -143,6 +143,10 @@ module Bunny
143
143
  attr_reader :work_pool
144
144
  # @return [Integer] Next publisher confirmations sequence index
145
145
  attr_reader :next_publish_seq_no
146
+ # @return [Integer] Offset for the confirmations sequence index.
147
+ # This will be set to the current sequence index during automatic network failure recovery
148
+ # to keep the sequence monotonic for the user and abstract the reset from the protocol
149
+ attr_reader :delivery_tag_offset
146
150
  # @return [Hash<String, Bunny::Queue>] Queue instances declared on this channel
147
151
  attr_reader :queues
148
152
  # @return [Hash<String, Bunny::Exchange>] Exchange instances declared on this channel
@@ -374,7 +378,7 @@ module Bunny
374
378
  # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
375
379
  # @api public
376
380
  def default_exchange
377
- Exchange.default(self)
381
+ @default_exchange ||= Exchange.default(self)
378
382
  end
379
383
 
380
384
  # Declares a headers exchange or looks it up in the cache of previously
@@ -392,7 +396,7 @@ module Bunny
392
396
  # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
393
397
  # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
394
398
  def exchange(name, opts = {})
395
- Exchange.new(self, opts.fetch(:type, :direct), name, opts)
399
+ find_exchange(name) || Exchange.new(self, opts.fetch(:type, :direct), name, opts)
396
400
  end
397
401
 
398
402
  # @endgroup
@@ -408,7 +412,7 @@ module Bunny
408
412
  # @option opts [Boolean] :durable (false) Should this queue be durable?
409
413
  # @option opts [Boolean] :auto-delete (false) Should this queue be automatically deleted when the last consumer disconnects?
410
414
  # @option opts [Boolean] :exclusive (false) Should this queue be exclusive (only can be used by this connection, removed when the connection is closed)?
411
- # @option opts [Boolean] :arguments ({}) Additional optional arguments (typically used by RabbitMQ extensions and plugins)
415
+ # @option opts [Hash] :arguments ({}) Additional optional arguments (typically used by RabbitMQ extensions and plugins)
412
416
  #
413
417
  # @return [Bunny::Queue] Queue that was declared or looked up in the cache
414
418
  # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
@@ -422,6 +426,73 @@ module Bunny
422
426
  register_queue(q)
423
427
  end
424
428
 
429
+ # Declares a new client-named quorum queue.
430
+ #
431
+ # @param [String] name Queue name. Empty (server-generated) names are not supported by this method.
432
+ # @param [Hash] opts Queue properties and other options. Durability, exclusivity, auto-deletion options will be ignored.
433
+ #
434
+ # @option opts [Hash] :arguments ({}) Additional optional arguments (typically used by RabbitMQ extensions and plugins)
435
+ #
436
+ # @return [Bunny::Queue] Queue that was declared
437
+ # @see #durable_queue
438
+ # @see #queue
439
+ # @api public
440
+ def quorum_queue(name, opts = {})
441
+ throw ArgumentError.new("quorum queue name must not be nil") if name.nil?
442
+ throw ArgumentError.new("quorum queue name must not be empty (server-named QQs do not make sense)") if name.empty?
443
+
444
+ durable_queue(name, Bunny::Queue::Types::QUORUM, opts)
445
+ end
446
+
447
+ # Declares a new client-named stream (that Bunny can use as if it was a queue).
448
+ # Note that Bunny would still use AMQP 0-9-1 to perform operations on this "queue".
449
+ # To use stream-specific operations and to gain from stream protocol efficiency and partitioning,
450
+ # use a Ruby client for the RabbitMQ stream protocol.
451
+ #
452
+ # @param [String] name Stream name. Empty (server-generated) names are not supported by this method.
453
+ # @param [Hash] opts Queue properties and other options. Durability, exclusivity, auto-deletion options will be ignored.
454
+ #
455
+ # @option opts [Hash] :arguments ({}) Additional optional arguments (typically used by RabbitMQ extensions and plugins)
456
+ #
457
+ #
458
+ # @return [Bunny::Queue] Queue that was declared
459
+ # @see #durable_queue
460
+ # @see #queue
461
+ # @api public
462
+ def stream(name, opts = {})
463
+ throw ArgumentError.new("stream name must not be nil") if name.nil?
464
+ throw ArgumentError.new("stream name must not be empty (server-named QQs do not make sense)") if name.empty?
465
+
466
+ durable_queue(name, Bunny::Queue::Types::STREAM, opts)
467
+ end
468
+
469
+ # Declares a new server-named queue that is automatically deleted when the
470
+ # connection is closed.
471
+ #
472
+ # @param [String] name Queue name. Empty (server-generated) names are not supported by this method.
473
+ # @param [Hash] opts Queue properties and other options. Durability, exclusivity, auto-deletion options will be ignored.
474
+ #
475
+ # @option opts [Hash] :arguments ({}) Additional optional arguments (typically used by RabbitMQ extensions and plugins)
476
+ #
477
+ # @return [Bunny::Queue] Queue that was declared
478
+ # @see #queue
479
+ # @api public
480
+ def durable_queue(name, type = "classic", opts = {})
481
+ throw ArgumentError.new("queue name must not be nil") if name.nil?
482
+ throw ArgumentError.new("queue name must not be empty (server-named durable queues do not make sense)") if name.empty?
483
+
484
+ final_opts = opts.merge({
485
+ :type => type,
486
+ :durable => true,
487
+ # exclusive or auto-delete QQs do not make much sense
488
+ :exclusive => false,
489
+ :auto_delete => false
490
+ })
491
+ q = find_queue(name) || Bunny::Queue.new(self, name, final_opts)
492
+
493
+ register_queue(q)
494
+ end
495
+
425
496
  # Declares a new server-named queue that is automatically deleted when the
426
497
  # connection is closed.
427
498
  #
@@ -1156,7 +1227,7 @@ module Bunny
1156
1227
 
1157
1228
  # @group Exchange operations (exchange.*)
1158
1229
 
1159
- # Declares a exchange using echange.declare AMQP 0.9.1 method.
1230
+ # Declares a exchange using exchange.declare AMQP 0.9.1 method.
1160
1231
  #
1161
1232
  # @param [String] name The name of the exchange. Note that LF and CR characters
1162
1233
  # will be stripped from the value.
@@ -1525,12 +1596,15 @@ module Bunny
1525
1596
 
1526
1597
  # Recovers publisher confirms mode. Used by the Automatic Network Failure
1527
1598
  # Recovery feature.
1599
+ # Set the offset to the previous publish sequence index as the protocol will reset the index to after recovery.
1528
1600
  #
1529
1601
  # @api plugin
1530
1602
  def recover_confirm_mode
1531
1603
  if using_publisher_confirmations?
1532
- @unconfirmed_set.clear
1533
- @delivery_tag_offset = @next_publish_seq_no - 1
1604
+ @unconfirmed_set_mutex.synchronize do
1605
+ @unconfirmed_set.clear
1606
+ @delivery_tag_offset = @next_publish_seq_no - 1
1607
+ end
1534
1608
  confirm_select(@confirms_callback)
1535
1609
  end
1536
1610
  end
@@ -1599,7 +1673,7 @@ module Bunny
1599
1673
 
1600
1674
  # @return [String] Brief human-readable representation of the channel
1601
1675
  def to_s
1602
- "#<#{self.class.name}:#{object_id} @id=#{self.number} @connection=#{@connection.to_s}> @open=#{open?}"
1676
+ "#<#{self.class.name}:#{object_id} @id=#{self.number} @connection=#{@connection.to_s} @open=#{open?}>"
1603
1677
  end
1604
1678
 
1605
1679
  def inspect
@@ -1663,7 +1737,7 @@ module Bunny
1663
1737
  if !pending_server_named_queue_declaration?
1664
1738
  # this response is for an outdated/overwritten
1665
1739
  # queue.declare, drop it
1666
- @logger.warn "Received a queue.declare-ok response for a mismatching queue (#{method.queue} instead of #{@pending_queue_declare_name}) on channel #{@id} possibly due to a timeout, ignoring it"
1740
+ @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"
1667
1741
  end
1668
1742
  end
1669
1743
  when AMQ::Protocol::Queue::DeleteOk then
@@ -1786,14 +1860,16 @@ module Bunny
1786
1860
  end
1787
1861
  end
1788
1862
 
1863
+ # Handle delivery tag offset calculations to keep the the delivery tag monotonic after a reset
1864
+ # due to automatic network failure recovery. @unconfirmed_set contains indices already offsetted.
1789
1865
  # @private
1790
1866
  def handle_ack_or_nack(delivery_tag_before_offset, multiple, nack)
1791
- delivery_tag = delivery_tag_before_offset + @delivery_tag_offset
1792
- confirmed_range_start = multiple ? @delivery_tag_offset + 1 : delivery_tag
1793
- confirmed_range_end = delivery_tag
1794
- confirmed_range = (confirmed_range_start..confirmed_range_end)
1795
-
1796
1867
  @unconfirmed_set_mutex.synchronize do
1868
+ delivery_tag = delivery_tag_before_offset + @delivery_tag_offset
1869
+ confirmed_range_start = multiple ? @unconfirmed_set.min : delivery_tag
1870
+ confirmed_range_end = delivery_tag
1871
+ confirmed_range = (confirmed_range_start..confirmed_range_end)
1872
+
1797
1873
  if nack
1798
1874
  @nacked_set.merge(@unconfirmed_set & confirmed_range)
1799
1875
  end
@@ -86,12 +86,12 @@ module Bunny
86
86
 
87
87
  # @return [String] More detailed human-readable string representation of this consumer
88
88
  def inspect
89
- "#<#{self.class.name}:#{object_id} @channel_id=#{@channel.number} @queue=#{self.queue_name}> @consumer_tag=#{@consumer_tag} @exclusive=#{@exclusive} @no_ack=#{@no_ack}>"
89
+ "#<#{self.class.name}:#{object_id} @channel_id=#{@channel.number} @queue=#{self.queue_name} @consumer_tag=#{@consumer_tag} @exclusive=#{@exclusive} @no_ack=#{@no_ack}>"
90
90
  end
91
91
 
92
92
  # @return [String] Brief human-readable string representation of this consumer
93
93
  def to_s
94
- "#<#{self.class.name}:#{object_id} @channel_id=#{@channel.number} @queue=#{self.queue_name}> @consumer_tag=#{@consumer_tag}>"
94
+ "#<#{self.class.name}:#{object_id} @channel_id=#{@channel.number} @queue=#{self.queue_name} @consumer_tag=#{@consumer_tag}>"
95
95
  end
96
96
 
97
97
  # @return [Boolean] true if this consumer uses automatic acknowledgement mode
@@ -70,7 +70,7 @@ module Bunny
70
70
  return if !(wait_for_workers && @shutdown_timeout && was_running)
71
71
 
72
72
  @shutdown_mutex.synchronize do
73
- @shutdown_conditional.wait(@shutdown_mutex, @shutdown_timeout)
73
+ @shutdown_conditional.wait(@shutdown_mutex, @shutdown_timeout) if busy?
74
74
  end
75
75
  end
76
76
 
@@ -32,6 +32,9 @@ module Bunny
32
32
  socket.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, true)
33
33
  end
34
34
  socket.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true) if options.fetch(:keepalive, true)
35
+ socket.instance_eval do
36
+ @__bunny_socket_eof_flag__ = false
37
+ end
35
38
  socket.extend self
36
39
  socket.options = { :host => host, :port => port }.merge(options)
37
40
  socket
@@ -37,7 +37,7 @@ module Bunny
37
37
  @channel = channel
38
38
  end
39
39
 
40
- # Iterates over the delivery properties
40
+ # Iterates over delivery properties
41
41
  # @see Enumerable#each
42
42
  def each(*args, &block)
43
43
  @hash.each(*args, &block)
data/lib/bunny/queue.rb CHANGED
@@ -11,6 +11,23 @@ module Bunny
11
11
  # API
12
12
  #
13
13
 
14
+ module Types
15
+ QUORUM = "quorum"
16
+ CLASSIC = "classic"
17
+ STREAM = "stream"
18
+
19
+ KNOWN = [CLASSIC, QUORUM, STREAM]
20
+
21
+ def self.known?(q_type)
22
+ KNOWN.include?(q_type)
23
+ end
24
+ end
25
+
26
+ module XArgs
27
+ MAX_LENGTH = "x-max-length",
28
+ QUEUE_TYPE = "x-queue-type"
29
+ end
30
+
14
31
  # @return [Bunny::Channel] Channel this queue uses
15
32
  attr_reader :channel
16
33
  # @return [String] Queue name
@@ -25,7 +42,8 @@ module Bunny
25
42
  # @option opts [Boolean] :durable (false) Should this queue be durable?
26
43
  # @option opts [Boolean] :auto_delete (false) Should this queue be automatically deleted when the last consumer disconnects?
27
44
  # @option opts [Boolean] :exclusive (false) Should this queue be exclusive (only can be used by this connection, removed when the connection is closed)?
28
- # @option opts [Boolean] :arguments ({}) Additional optional arguments (typically used by RabbitMQ extensions and plugins)
45
+ # @option opts [String] :type (nil) Type of the declared queue (classic, quorum or stream)
46
+ # @option opts [Hash] :arguments (nil) Additional optional arguments (typically used by RabbitMQ extensions and plugins)
29
47
  #
30
48
  # @see Bunny::Channel#queue
31
49
  # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
@@ -42,7 +60,17 @@ module Bunny
42
60
  @exclusive = @options[:exclusive]
43
61
  @server_named = @name.empty?
44
62
  @auto_delete = @options[:auto_delete]
45
- @arguments = @options[:arguments]
63
+ @type = @options[:type]
64
+
65
+ @arguments = if @type and !@type.empty? then
66
+ (@options[:arguments] || {}).merge({XArgs::QUEUE_TYPE => @type})
67
+ else
68
+ @options[:arguments]
69
+ end
70
+ verify_type!(@arguments)
71
+ # reassigns updated and verified arguments because Bunny::Channel#declare_queue
72
+ # accepts a map of options
73
+ @options[:arguments] = @arguments
46
74
 
47
75
  @bindings = Array.new
48
76
 
@@ -158,9 +186,6 @@ module Bunny
158
186
  # @option opts [Boolean] :ack (false) [DEPRECATED] Use :manual_ack instead
159
187
  # @option opts [Boolean] :manual_ack (false) Will this consumer use manual acknowledgements?
160
188
  # @option opts [Boolean] :exclusive (false) Should this consumer be exclusive for this queue?
161
- # @option opts [Boolean] :block (false) Should the call block the calling thread? This option can be useful for keeping the main thread of
162
- # a script alive. It is incompatible with automatic connection recovery
163
- # and is not generally recommended.
164
189
  # @option opts [#call] :on_cancellation Block to execute when this consumer is cancelled remotely (e.g. via the RabbitMQ Management plugin)
165
190
  # @option opts [String] :consumer_tag Unique consumer identifier. It is usually recommended to let Bunny generate it for you.
166
191
  # @option opts [Hash] :arguments ({}) Additional (optional) arguments, typically used by RabbitMQ extensions
@@ -392,5 +417,11 @@ module Bunny
392
417
  h
393
418
  end
394
419
  end
420
+
421
+ def verify_type!(args)
422
+ q_type = (args || {})["x-queue-type"]
423
+ throw ArgumentError.new(
424
+ "unsupported queue type #{q_type.inspect}, supported ones: #{Types::KNOWN.join(', ')}") if (q_type and !Types.known?(q_type))
425
+ end
395
426
  end
396
427
  end
@@ -9,17 +9,17 @@ module Bunny
9
9
  # @private
10
10
  class ReaderLoop
11
11
 
12
- def initialize(transport, session, session_thread)
13
- @transport = transport
14
- @session = session
15
- @session_thread = session_thread
16
- @logger = @session.logger
12
+ def initialize(transport, session, session_error_handler)
13
+ @transport = transport
14
+ @session = session
15
+ @session_error_handler = session_error_handler
16
+ @logger = @session.logger
17
17
 
18
- @mutex = Mutex.new
18
+ @mutex = Mutex.new
19
19
 
20
- @stopping = false
21
- @stopped = false
22
- @network_is_down = false
20
+ @stopping = false
21
+ @stopped = false
22
+ @network_is_down = false
23
23
  end
24
24
 
25
25
 
@@ -37,7 +37,8 @@ module Bunny
37
37
  begin
38
38
  break if @mutex.synchronize { @stopping || @stopped || @network_is_down }
39
39
  run_once
40
- rescue AMQ::Protocol::EmptyResponseError, IOError, SystemCallError, Timeout::Error => e
40
+ rescue AMQ::Protocol::EmptyResponseError, IOError, SystemCallError, Timeout::Error,
41
+ OpenSSL::OpenSSLError => e
41
42
  break if terminate? || @session.closing? || @session.closed?
42
43
 
43
44
  @network_is_down = true
@@ -46,7 +47,7 @@ module Bunny
46
47
  @session.handle_network_failure(e)
47
48
  else
48
49
  log_exception(e)
49
- @session_thread.raise(Bunny::NetworkFailure.new("detected a network failure: #{e.message}", e))
50
+ @session_error_handler.raise(Bunny::NetworkFailure.new("detected a network failure: #{e.message}", e))
50
51
  end
51
52
  rescue ShutdownSignal => _
52
53
  @mutex.synchronize { @stopping = true }
@@ -57,7 +58,7 @@ module Bunny
57
58
  log_exception(e)
58
59
 
59
60
  @network_is_down = true
60
- @session_thread.raise(Bunny::NetworkFailure.new("caught an unexpected exception in the network loop: #{e.message}", e))
61
+ @session_error_handler.raise(Bunny::NetworkFailure.new("caught an unexpected exception in the network loop: #{e.message}", e))
61
62
  end
62
63
  rescue Errno::EBADF => _ebadf
63
64
  break if terminate?
@@ -115,7 +116,14 @@ module Bunny
115
116
  end
116
117
 
117
118
  def join
118
- @thread.join if @thread
119
+ # Thread#join can/would trigger a re-raise of an unhandled exception in this thread.
120
+ # In addition, Thread.handle_interrupt can be used by other libraries or application code
121
+ # that would make this join operation fail with an obscure exception.
122
+ # So we try to save everyone some really unpleasant debugging time by introducing
123
+ # this condition which typically would not evaluate to true anyway.
124
+ #
125
+ # See ruby-amqp/bunny#589 and ruby-amqp/bunny#590 for background.
126
+ @thread.join if @thread && @thread != Thread.current
119
127
  end
120
128
 
121
129
  def kill