hutch 1.3.1 → 2.0.0.rc1

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +111 -7
  3. data/LICENSE +1 -1
  4. data/README.md +42 -42
  5. data/bin/ci/before_build_docker.sh +2 -7
  6. data/lib/hutch/acknowledgements/nack_on_all_failures.rb +1 -1
  7. data/lib/hutch/adapters/bunny.rb +25 -1
  8. data/lib/hutch/adapters/march_hare.rb +21 -1
  9. data/lib/hutch/broker.rb +82 -27
  10. data/lib/hutch/cli.rb +1 -4
  11. data/lib/hutch/config.rb +3 -3
  12. data/lib/hutch/consumer.rb +12 -3
  13. data/lib/hutch/error_handlers.rb +0 -1
  14. data/lib/hutch/serializers/json.rb +3 -3
  15. data/lib/hutch/tracers/datadog.rb +2 -2
  16. data/lib/hutch/version.rb +1 -1
  17. data/lib/hutch/worker.rb +43 -6
  18. data/lib/yard-settings/yard-settings.rb +2 -2
  19. data/spec/hutch/broker_spec.rb +189 -11
  20. data/spec/hutch/cli_spec.rb +12 -9
  21. data/spec/hutch/consumer_spec.rb +11 -0
  22. data/spec/hutch/error_handlers/airbrake_spec.rb +4 -1
  23. data/spec/hutch/error_handlers/bugsnag_spec.rb +1 -1
  24. data/spec/hutch/error_handlers/honeybadger_spec.rb +1 -1
  25. data/spec/hutch/error_handlers/logger_spec.rb +1 -1
  26. data/spec/hutch/error_handlers/rollbar_spec.rb +1 -1
  27. data/spec/hutch/error_handlers/sentry_spec.rb +1 -1
  28. data/spec/hutch/message_spec.rb +1 -1
  29. data/spec/hutch/tracers/datadog_spec.rb +1 -1
  30. data/spec/hutch/worker_spec.rb +82 -12
  31. data/spec/integration/channel_recovery_spec.rb +187 -0
  32. data/spec/integration/publish_consume_spec.rb +47 -0
  33. metadata +12 -41
  34. data/.github/workflows/test.yml +0 -56
  35. data/.gitignore +0 -10
  36. data/.rspec +0 -1
  37. data/.yardopts +0 -5
  38. data/Gemfile +0 -35
  39. data/Guardfile +0 -14
  40. data/Rakefile +0 -21
  41. data/examples/consumer.rb +0 -13
  42. data/examples/producer.rb +0 -10
  43. data/hutch.gemspec +0 -27
  44. data/lib/hutch/error_handlers/sentry_raven.rb +0 -31
  45. data/spec/hutch/error_handlers/sentry_raven_spec.rb +0 -37
  46. data/spec/spec_helper.rb +0 -42
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 02b687d56ec0d56dc49c07722e3e55c3b86ca274008394209b3fe60d6ecb8bf4
4
- data.tar.gz: a6c38c9b5f26944f7abb3b078d4cf693bb29ed2fb08987b7f4bc5e4fe2a70f78
3
+ metadata.gz: 8978faf08cf85e68cc957d91ff3059b0b3fecb73e018a80ec551c9547be9c99a
4
+ data.tar.gz: 2c1f461463321e705e2464373093135bf307b68dac9f08b284e8c5f6c9e710db
5
5
  SHA512:
6
- metadata.gz: 85513bc753214f4a077c4a50510519082e3f5bc8aee75cba1df09461be9723e4a8d438a97f3c6bd3025fb59a0d7008adb5577c7faee082367ddd85f7239f748a
7
- data.tar.gz: f0d6fcc77c94a0349104243c3daac3e2f4fe057ce96a4b251c0577969fcaf1b69860ca3eadd163aac353275bbe517847573f6064ea46cad70aa8979004c6e9f7
6
+ metadata.gz: 8900ac9233d8849f789b2f598e8109d8bcc7aab3bc7870c00d881e47aaa6f3a3722b906a018c8c8f6dd0b69258a15f7f7654027a216429a9bfe2639f53070100
7
+ data.tar.gz: e3c8b5ac42f24483a773167eb79d01e9e29e71859c0a78ccf01d59b018ec1f57a1a5c26c8da47ad9ebb743b04fed9d67989802eb0f495bd894c1b3c51cb830c3
data/CHANGELOG.md CHANGED
@@ -1,8 +1,112 @@
1
- ## 1.3.2 (in development)
1
+ # Hutch Change Log
2
2
 
3
- No changes yet.
3
+ ## 2.0.0 (TBD)
4
4
 
5
- ## 1.3.1 (Dec 11, 2024)
5
+ ### Breaking Changes
6
+
7
+ * Ruby `3.2` or JRuby `10` is now required
8
+ * The Datadog tracer now depends on the [`datadog`](https://github.com/DataDog/dd-trace-rb)
9
+ gem, which replaces the EOL `ddtrace`
10
+ ([migration guide](https://github.com/DataDog/dd-trace-rb/blob/master/docs/UpgradeGuide2.md))
11
+ * `Hutch::ErrorHandlers::SentryRaven` has been replaced by
12
+ `Hutch::ErrorHandlers::Sentry`, backed by `sentry-ruby`
13
+ ([migration guide](https://docs.sentry.io/platforms/ruby/migration/))
14
+ * Messages are serialized with the stdlib `json` instead of `multi_json`, which
15
+ means that `MultiJson` configuration such as `MultiJson.use(:oj)` no longer
16
+ affects Hutch
17
+
18
+ ### Consumer Recovery After a RabbitMQ 4.3 Quorum Queue Consumer Timeout
19
+
20
+ RabbitMQ 4.3 quorum queues cancel a timed out consumer with `basic.cancel`
21
+ instead of closing the channel. Hutch 1.4.0 does not react to that, so on 4.3
22
+ a consumer stops consuming its queue until the worker restarts.
23
+
24
+ Workers now re-subscribe consumers cancelled by the server, but only if
25
+ the queue still exists: a `basic.cancel` carries no reason, so a consumer
26
+ timeout is indistinguishable from a queue deletion. The consumer channel is
27
+ replaced in the process, so the deliveries the cancelled consumer still held
28
+ are requeued instead of dropped.
29
+
30
+ Handlers that are still running get a graceful exit to finish before
31
+ the old channel is closed, and their acknowledgements are dropped rather than
32
+ sent on the new channel, where their delivery tags mean nothing.
33
+
34
+ ### Connection URIs Are Now Percent-Decoded
35
+
36
+ The virtual host, username and password of a connection URI are now decoded, as
37
+ [the URI specification](https://www.rabbitmq.com/docs/uri-spec) requires.
38
+ `amqp://host/%2F` connects to the default virtual host instead of one literally
39
+ named `%2F`, and credentials containing a space, `#` or `@` authenticate again.
40
+
41
+ Reported by @deissimon.
42
+
43
+ GitHub issue: [#422](https://github.com/ruby-amqp/hutch/issues/422)
44
+
45
+ ### Fixed Two JRuby Regressions
46
+
47
+ Opening a channel raised, and the `march_hare` adapter did not declare the
48
+ exchange it instantiated.
49
+
50
+ GitHub issue: [#427](https://github.com/ruby-amqp/hutch/issues/427)
51
+
52
+ ### The JRuby Variant Is Revived
53
+
54
+ The `java` platform gem, which depends on March Hare instead of Bunny, was last
55
+ published as `0.25.0` in January 2018 and has seen little more than CI version
56
+ bumps since. This release requires JRuby `10` and `march_hare` `4.7.0`, adds a
57
+ JRuby CI job, and publishes the gem again.
58
+
59
+ Recovering a channel the broker closed remains Bunny-only: `MarchHare::Channel`
60
+ has no `on_error` callback and no `reopen`.
61
+
62
+ ## 1.4.0 (Apr 7, 2026)
63
+
64
+ ### Ruby 3.0 is Now Required
65
+
66
+ This version adopts [Bunny `3.x`](https://github.com/ruby-amqp/bunny/releases/tag/3.0.0)
67
+ and as a result, requires Ruby 3.0.
68
+
69
+ ### `without_namespace` Consumer DSL Option
70
+
71
+ Consumers can now opt out of the automatic namespace prefix
72
+ for their queue name using the `without_namespace` DSL method:
73
+
74
+ ``` ruby
75
+ class DeadLetterConsumer
76
+ include Hutch::Consumer
77
+ consume 'dead.letters'
78
+ queue_name 'deadletter'
79
+ without_namespace
80
+ end
81
+ ```
82
+
83
+ Originally contributed by @tlloydthwaites.
84
+
85
+ GitHub issue: [#393](https://github.com/ruby-amqp/hutch/pull/393)
86
+
87
+ ### Consumer Channel Recovery After Delivery Acknowledgement Timeout
88
+
89
+ Hutch now automatically "recovers" (reopens) consumer channels closed by RabbitMQ
90
+ due to a [delivery acknowledgement timeout](https://www.rabbitmq.com/docs/consumers#acknowledgement-timeout).
91
+
92
+ Note that this exception indicates a potential issue on Hutch's end:
93
+ a delivery was not acknowledged in time (30 minutes by default). Even then,
94
+ recovering the channel in this specific scenario is an operational improvement.
95
+
96
+ Contributed by @Garaio-REM.
97
+
98
+ GitHub issue: [#414](https://github.com/ruby-amqp/hutch/pull/414)
99
+
100
+ ### Migrated Datadog Tracer From the `ddtrace` Gem to `datadog`
101
+
102
+ The Datadog tracer now uses the `datadog` gem instead of the deprecated `ddtrace`.
103
+ The `ddtrace` gem is still supported but emits a deprecation warning at load time.
104
+ Support for `ddtrace` will be removed in Hutch 2.0.
105
+
106
+ ### Deprecated `SentryRaven` Error Handler
107
+
108
+ `Hutch::ErrorHandlers::SentryRaven` now emits a deprecation warning and will be
109
+ removed in Hutch 2.0. Use `Hutch::ErrorHandlers::Sentry` (backed by `sentry-ruby`) instead.
6
110
 
7
111
  ### Rails 8.x Compatibility
8
112
 
@@ -19,7 +123,7 @@ GitHub issue: [#392](https://github.com/ruby-amqp/hutch/pull/392)
19
123
 
20
124
  ### Relaxed ActiveSupport Dependency Constraints
21
125
 
22
- Contributed by @drobny.
126
+ Contributed by drobny.
23
127
 
24
128
  GitHub issue: [#402](https://github.com/ruby-amqp/hutch/pull/402)
25
129
 
@@ -93,7 +197,7 @@ contains potentially breaking changes.
93
197
 
94
198
  This means **some defaults introduced in `0.28.0` ([gocardless/hutch#341](https://github.com/gocardless/hutch/pull/341)) were reverted**.
95
199
  The user has to opt in to configure the queue type and mode and other [optional arguments](https://www.rabbitmq.com/queues.html#optional-arguments) they need to use.
96
- Most optional arguments can be set via [policies](https://www.rabbitmq.com/parameters.html#policies) which is always the recommended approach.
200
+ Most optional arguments can be set via [policies](https://www.rabbitmq.com/parameters.html#policies) which is always the recommended approach.
97
201
  Queue type, unfortunately, is not one of them as different queue types have completely different
98
202
  implementation details, on disk data formats and so on.
99
203
 
@@ -106,7 +210,7 @@ contains potentially breaking changes.
106
210
  # when in doubt, prefer using a policy to this DSL
107
211
  # https://www.rabbitmq.com/parameters.html#policies
108
212
  arguments 'x-key': :value
109
-
213
+
110
214
  quorum_queue
111
215
  end
112
216
  ```
@@ -120,7 +224,7 @@ contains potentially breaking changes.
120
224
  # when in doubt, prefer using a policy to this DSL
121
225
  # https://www.rabbitmq.com/parameters.html#policies
122
226
  arguments 'x-key': :value
123
-
227
+
124
228
  lazy_queue
125
229
  classic_queue
126
230
  end
data/LICENSE CHANGED
@@ -1,5 +1,5 @@
1
1
  Copyright (c) 2013-2016 GoCardless
2
- Copyright (c) 2016-2021 Hutch contributors
2
+ Copyright (c) 2016-2026 Hutch contributors
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person
5
5
  obtaining a copy of this software and associated documentation
data/README.md CHANGED
@@ -1,7 +1,5 @@
1
- ![](http://cl.ly/image/3h0q3F3G142K/hutch.png)
2
-
3
- [![Gem Version](https://badge.fury.io/rb/hutch.svg)](http://badge.fury.io/rb/hutch)
4
- [![Code Climate](https://codeclimate.com/github/gocardless/hutch.svg)](https://codeclimate.com/github/gocardless/hutch)
1
+ [![Gem Version](https://badge.fury.io/rb/hutch.svg)](https://badge.fury.io/rb/hutch)
2
+ [![CI](https://github.com/ruby-amqp/hutch/actions/workflows/test.yml/badge.svg)](https://github.com/ruby-amqp/hutch/actions/workflows/test.yml)
5
3
 
6
4
  Hutch is a Ruby library for enabling asynchronous inter-service communication
7
5
  in a service-oriented architecture, using RabbitMQ.
@@ -23,6 +21,8 @@ gem install hutch
23
21
  - [Message Processing Tracers](#message-processing-tracers)
24
22
  - [Running Hutch](#running-hutch)
25
23
  - [Loading Consumers](#loading-consumers)
24
+ - [Consumer Groups](#consumer-groups)
25
+ - [Loading Consumers Manually (One-by-One)](#loading-consumers-manually-one-by-one)
26
26
  - [Stopping Hutch](#stopping-hutch)
27
27
  - [Producers](#producers)
28
28
  - [Producer Configuration](#producer-configuration)
@@ -38,8 +38,8 @@ gem install hutch
38
38
 
39
39
  ## Requirements
40
40
 
41
- - Hutch requires Ruby 2.4+ or JRuby 9K.
42
- - Hutch requires RabbitMQ 3.3 or later.
41
+ - Hutch requires Ruby 3.2+ or JRuby 10+
42
+ - Hutch requires RabbitMQ 3.13 or later
43
43
 
44
44
  ## Overview
45
45
 
@@ -52,7 +52,7 @@ With Hutch, consumers are stored in separate files and include the `Hutch::Consu
52
52
  They are then loaded by a command line runner which connects to RabbitMQ, sets up queues and bindings,
53
53
  and so on. Publishers connect to RabbitMQ via `Hutch.connect` and publish using `Hutch.publish`.
54
54
 
55
- Hutch uses [Bunny](http://rubybunny.info) or [March Hare](http://rubymarchhare.info)
55
+ Hutch uses [Bunny](https://github.com/ruby-amqp/bunny) or [March Hare](https://github.com/ruby-amqp/march_hare)
56
56
  (on JRuby) under the hood.
57
57
 
58
58
  ### Project Maturity
@@ -79,7 +79,7 @@ message[:id] # => "02ABCXYZ"
79
79
  To subscribe to a topic, pass a routing key to `consume` in the class
80
80
  definition. To bind to multiple routing keys, simply pass extra routing keys
81
81
  in as additional arguments. Refer to the [RabbitMQ docs on topic exchanges
82
- ](http://www.rabbitmq.com/tutorials/tutorial-five-ruby.html) for more information
82
+ ](https://www.rabbitmq.com/tutorials/tutorial-five-ruby) for more information
83
83
  about how to use routing keys. Here's an example consumer:
84
84
 
85
85
  ```ruby
@@ -110,10 +110,10 @@ end
110
110
 
111
111
  It is possible to set some custom options to consumer's queue explicitly.
112
112
  This example sets the consumer's queue as a
113
- [quorum queue](https://www.rabbitmq.com/quorum-queues.html)
114
- and to operate in the [lazy mode](https://www.rabbitmq.com/lazy-queues.html).
113
+ [quorum queue](https://www.rabbitmq.com/docs/quorum-queues)
114
+ and to operate in the [lazy mode](https://www.rabbitmq.com/docs/lazy-queues).
115
115
  The `initial_group_size`
116
- [argument](https://www.rabbitmq.com/quorum-queues.html#replication-factor) is
116
+ [argument](https://www.rabbitmq.com/docs/quorum-queues#replication-factor) is
117
117
  optional.
118
118
 
119
119
  ```ruby
@@ -142,11 +142,11 @@ end
142
142
  ```
143
143
 
144
144
  This sets the `x-max-length` header. For more details, see the [RabbitMQ
145
- documentation on Queue Length Limit](https://www.rabbitmq.com/maxlength.html). To find out more
146
- about custom queue arguments, consult the [RabbitMQ documentation on AMQP Protocol Extensions](https://www.rabbitmq.com/extensions.html).
145
+ documentation on Queue Length Limit](https://www.rabbitmq.com/docs/maxlength). To find out more
146
+ about custom queue arguments, consult the [RabbitMQ documentation on AMQP Protocol Extensions](https://www.rabbitmq.com/docs/extensions).
147
147
 
148
148
  Consumers can write to Hutch's log by calling the logger method. The logger method returns
149
- a [Logger object](http://ruby-doc.org/stdlib-2.1.2/libdoc/logger/rdoc/Logger.html).
149
+ a [Logger object](https://docs.ruby-lang.org/en/3.0/Logger.html).
150
150
 
151
151
  ```ruby
152
152
  class FailedPaymentConsumer
@@ -174,7 +174,7 @@ client_logger = Logger.new("/path/to/bunny.log")
174
174
  Hutch::Config.set(:client_logger, client_logger)
175
175
  ```
176
176
 
177
- See this [RabbitMQ tutorial on topic exchanges](http://www.rabbitmq.com/tutorials/tutorial-five-ruby.html)
177
+ See this [RabbitMQ tutorial on topic exchanges](https://www.rabbitmq.com/tutorials/tutorial-five-ruby)
178
178
  to learn more.
179
179
 
180
180
  ### Message Processing Tracers
@@ -206,7 +206,7 @@ usage: hutch [options]
206
206
  --mq-host HOST Set the RabbitMQ host
207
207
  --mq-port PORT Set the RabbitMQ port
208
208
  -t, --[no-]mq-tls Use TLS for the AMQP connection
209
- --mq-tls-cert FILE Certificate for TLS client verification
209
+ --mq-tls-cert FILE Certificate for TLS client verification
210
210
  --mq-tls-key FILE Private key for TLS client verification
211
211
  --mq-exchange EXCHANGE Set the RabbitMQ exchange
212
212
  --mq-vhost VHOST Set the RabbitMQ vhost
@@ -242,13 +242,13 @@ in the config file, allowing for easy customization.
242
242
 
243
243
  Using Hutch with a Rails app is simple. Either start Hutch in the working
244
244
  directory of a Rails app, or pass the path to a Rails app in with the
245
- `--require` option. Consumers defined in Rails apps should be placed with in
245
+ `--require` option. Consumers defined in Rails apps should be placed within
246
246
  the `app/consumers/` directory, to allow them to be auto-loaded when Rails
247
247
  boots.
248
248
 
249
- If you're using the new Zeitwerk autoloader (enabled by default in Rails 6)
250
- and the consumers are not loaded in development environment you will need to
251
- trigger the autoloading in an initializer with
249
+ If you're using the Zeitwerk autoloader (the default since Rails 6)
250
+ and the consumers are not loaded in the development environment, you will need to
251
+ trigger eager loading in an initializer with
252
252
 
253
253
  ```ruby
254
254
  ::Zeitwerk::Loader.eager_load_all
@@ -309,8 +309,6 @@ kill -SIGTERM 456 # or kill -15 456
309
309
  kill -SIGQUIT 789 # or kill -3 789
310
310
  ```
311
311
 
312
- ![](http://g.recordit.co/wyCdzG9Kh3.gif)
313
-
314
312
  ## Producers
315
313
 
316
314
  Hutch includes a `publish` method for sending messages to Hutch consumers. When
@@ -334,7 +332,7 @@ Hutch::Config.set(:mq_exchange, 'name')
334
332
  ### Publisher Confirms
335
333
 
336
334
  For maximum message reliability when producing messages, you can force Hutch to use
337
- [Publisher Confirms](https://www.rabbitmq.com/confirms.html) and wait for a confirmation
335
+ [Publisher Confirms](https://www.rabbitmq.com/docs/confirms) and wait for a confirmation
338
336
  after every message published. This is the safest possible option for publishers
339
337
  but also results in a **significant throughput drop**.
340
338
 
@@ -354,25 +352,27 @@ send messages to Hutch.
354
352
  - Hutch works with topic exchanges, check the producer is also using topic
355
353
  exchanges.
356
354
  - Use message routing keys that match those used in your Hutch consumers.
357
- - Be sure your exchanges are marked as durable. In the Ruby AMQP gem, this is
358
- done by passing `durable: true` to the exchange creation method.
359
- - Publish messages as persistent.
360
- - Using publisher confirms is highly recommended.
355
+ - Be sure your exchanges are marked as durable, by passing `durable: true`
356
+ to the exchange creation method
357
+ - Publish messages as persistent
358
+ - Using publisher confirms is highly recommended
361
359
 
362
360
  Here's an example of a well-behaved publisher, minus publisher confirms:
363
361
 
364
362
  ```ruby
365
- AMQP.connect(host: config[:host]) do |connection|
366
- channel = AMQP::Channel.new(connection)
367
- exchange = channel.topic(config[:exchange], durable: true)
363
+ require 'bunny'
368
364
 
369
- message = JSON.dump({ subject: 'Test', id: 'abc' })
370
- exchange.publish(message, routing_key: 'test', persistent: true)
371
- end
372
- ```
365
+ conn = Bunny.new(host: config[:host])
366
+ conn.start
373
367
 
374
- If using publisher confirms with amqp gem, see [this issue](https://github.com/ruby-amqp/amqp/issues/92)
375
- and [this gist](https://gist.github.com/3042381) for more info.
368
+ ch = conn.create_channel
369
+ exchange = ch.topic(config[:exchange], durable: true)
370
+
371
+ message = JSON.dump({ subject: 'Test', id: 'abc' })
372
+ exchange.publish(message, routing_key: 'test', persistent: true)
373
+
374
+ conn.close
375
+ ```
376
376
 
377
377
  ## Configuration
378
378
 
@@ -385,7 +385,7 @@ Known configuration parameters are:
385
385
  * `mq_host`: RabbitMQ hostname (default: `localhost`)
386
386
  * `mq_port`: RabbitMQ port (default: `5672`)
387
387
  * `mq_vhost`: vhost to use (default: `/`)
388
- * `mq_username`: username to use (default: `guest`, only can connect from localhost as of RabbitMQ 3.3.0)
388
+ * `mq_username`: username to use (default: `guest`, can only connect from localhost)
389
389
  * `mq_password`: password to use (default: `guest`)
390
390
  * `mq_tls`: should TLS be used? (default: `false`)
391
391
  * `mq_tls_cert`: path to client TLS certificate (public key)
@@ -407,7 +407,7 @@ Known configuration parameters are:
407
407
  exception happens, see classes in `Hutch::Acknowledgements`.
408
408
  * `mq_exchange`: exchange to use for publishing (default: `hutch`)
409
409
  * `mq_client_properties`: Bunny's [client properties](https://www.rabbitmq.com/docs/connections#capabilities) (default: `{}`)
410
- * `heartbeat`: [RabbitMQ heartbeat timeout](http://rabbitmq.com/heartbeats.html) (default: `30`)
410
+ * `heartbeat`: [RabbitMQ heartbeat timeout](https://www.rabbitmq.com/docs/heartbeats) (default: `30`)
411
411
  * `connection_timeout`: Bunny's socket open timeout (default: `11`)
412
412
  * `read_timeout`: Bunny's socket read timeout (default: `11`)
413
413
  * `write_timeout`: Bunny's socket write timeout (default: `11`)
@@ -534,7 +534,7 @@ Generate with
534
534
  <td>30</td>
535
535
  <td>Number</td>
536
536
  <td><tt>HUTCH_HEARTBEAT</tt></td>
537
- <td><p><a href="http://rabbitmq.com/heartbeats.html">RabbitMQ heartbeat timeout</a></p></td>
537
+ <td><p><a href="https://www.rabbitmq.com/docs/heartbeats">RabbitMQ heartbeat timeout</a></p></td>
538
538
  </tr>
539
539
  <tr>
540
540
  <td><tt>channel_prefetch</tt></td>
@@ -590,7 +590,7 @@ Generate with
590
590
  <td>11</td>
591
591
  <td>Number</td>
592
592
  <td><tt>HUTCH_GRACEFUL_EXIT_TIMEOUT</tt></td>
593
- <td><p>FIXME: DOCUMENT THIS</p></td>
593
+ <td><p>Timeout (in seconds) for consumer threads to finish before being killed during graceful shutdown</p></td>
594
594
  </tr>
595
595
  <tr>
596
596
  <td><tt>consumer_pool_size</tt></td>
@@ -646,7 +646,7 @@ Generate with
646
646
  <td>false</td>
647
647
  <td>Boolean</td>
648
648
  <td><tt>HUTCH_FORCE_PUBLISHER_CONFIRMS</tt></td>
649
- <td><p>Enables publisher confirms, forces Hutch::Broker#wait_for_confirms for</p></td>
649
+ <td><p>Enables publisher confirms, forces Hutch::Broker#wait_for_confirms for every publish</p></td>
650
650
  </tr>
651
651
  <tr>
652
652
  <td><tt>enable_http_api_use</tt></td>
@@ -681,7 +681,7 @@ Generate with
681
681
  <td>''</td>
682
682
  <td>String</td>
683
683
  <td><tt>HUTCH_GROUP</tt></td>
684
- <td></td>
684
+ <td><p>Consumer group to load (see Consumer Groups)</p></td>
685
685
  </tr>
686
686
  </tbody>
687
687
  </table>
@@ -1,11 +1,6 @@
1
1
  #!/bin/sh
2
2
 
3
3
  CTL=${MARCH_HARE_RABBITMQCTL:="docker exec rabbitmq rabbitmqctl"}
4
- PLUGINS=${MARCH_HARE_RABBITMQ_PLUGINS:="docker exec rabbitmq rabbitmq-plugins"}
5
-
6
- $PLUGINS enable rabbitmq_management
7
-
8
- sleep 3
9
4
 
10
5
  # guest:guest has full access to /
11
6
 
@@ -14,7 +9,7 @@ $CTL add_vhost /
14
9
  $CTL set_permissions -p / guest ".*" ".*" ".*"
15
10
 
16
11
  # Reduce retention policy for faster publishing of stats
17
- $CTL eval 'supervisor2:terminate_child(rabbit_mgmt_sup_sup, rabbit_mgmt_sup), application:set_env(rabbitmq_management, sample_retention_policies, [{global, [{605, 1}]}, {basic, [{605, 1}]}, {detailed, [{10, 1}]}]), rabbit_mgmt_sup_sup:start_child().'
18
- $CTL eval 'supervisor2:terminate_child(rabbit_mgmt_agent_sup_sup, rabbit_mgmt_agent_sup), application:set_env(rabbitmq_management_agent, sample_retention_policies, [{global, [{605, 1}]}, {basic, [{605, 1}]}, {detailed, [{10, 1}]}]), rabbit_mgmt_agent_sup_sup:start_child().'
12
+ $CTL eval 'supervisor2:terminate_child(rabbit_mgmt_sup_sup, rabbit_mgmt_sup), application:set_env(rabbitmq_management, sample_retention_policies, [{global, [{605, 1}]}, {basic, [{605, 1}]}, {detailed, [{10, 1}]}]), rabbit_mgmt_sup_sup:start_child().' || true
13
+ $CTL eval 'supervisor2:terminate_child(rabbit_mgmt_agent_sup_sup, rabbit_mgmt_agent_sup), application:set_env(rabbitmq_management_agent, sample_retention_policies, [{global, [{605, 1}]}, {basic, [{605, 1}]}, {detailed, [{10, 1}]}]), rabbit_mgmt_agent_sup_sup:start_child().' || true
19
14
 
20
15
  sleep 3
@@ -10,7 +10,7 @@ module Hutch
10
10
  prefix = "message(#{properties.message_id || '-'}): "
11
11
  logger.debug "#{prefix} nacking message"
12
12
 
13
- broker.nack(delivery_info.delivery_tag)
13
+ broker.nack(delivery_info.delivery_tag, channel: delivery_info.channel)
14
14
 
15
15
  true
16
16
  end
@@ -1,17 +1,21 @@
1
1
  require 'bunny'
2
2
  require 'forwardable'
3
3
 
4
+ require 'hutch/logging'
5
+
4
6
  module Hutch
5
7
  module Adapters
6
8
  class BunnyAdapter
7
9
  extend Forwardable
10
+ include Logging
8
11
 
9
12
  DEFAULT_VHOST = Bunny::Session::DEFAULT_VHOST
10
13
 
11
14
  ConnectionRefused = Bunny::TCPConnectionFailed
15
+ ChannelAlreadyClosed = Bunny::ChannelAlreadyClosed
12
16
  PreconditionFailed = Bunny::PreconditionFailed
13
17
 
14
- def_delegators :@connection, :start, :disconnect, :close, :create_channel, :open?
18
+ def_delegators :@connection, :start, :disconnect, :close, :create_channel, :open?, :recover_channel_topology
15
19
 
16
20
  def initialize(opts={})
17
21
  @connection = Bunny.new(opts)
@@ -25,6 +29,26 @@ module Hutch
25
29
  ch.prefetch(prefetch) if prefetch
26
30
  end
27
31
 
32
+ def queue_exists?(name)
33
+ @connection.queue_exists?(name)
34
+ end
35
+
36
+ def install_channel_recovery(ch)
37
+ # A consumer timeout invalidates the delivery tag, so a handler that
38
+ # was still running acknowledges an unknown tag and closes the channel.
39
+ ch.on_error do |channel, close|
40
+ next unless close.delivery_ack_timeout? || close.unknown_delivery_tag?
41
+
42
+ begin
43
+ channel.reopen
44
+ recover_channel_topology(channel)
45
+ logger.warn "recovered consumer channel closed with '#{close.reply_text}'"
46
+ rescue => ex
47
+ logger.error "channel recovery failed: #{ex.class}: #{ex.message}"
48
+ end
49
+ end
50
+ end
51
+
28
52
  def current_timestamp
29
53
  Time.now.to_i
30
54
  end
@@ -9,6 +9,7 @@ module Hutch
9
9
  DEFAULT_VHOST = "/"
10
10
 
11
11
  ConnectionRefused = MarchHare::ConnectionRefused
12
+ ChannelAlreadyClosed = MarchHare::ChannelAlreadyClosed
12
13
  PreconditionFailed = MarchHare::PreconditionFailed
13
14
 
14
15
  def_delegators :@connection, :start, :disconnect, :close, :open?
@@ -25,6 +26,25 @@ module Hutch
25
26
  ch.prefetch = prefetch if prefetch
26
27
  end
27
28
 
29
+ # `MarchHare::Session` has no `queue_exists?` counterpart, and a failed
30
+ # passive declare closes the channel, hence the throwaway one.
31
+ def queue_exists?(name)
32
+ ch = @connection.create_channel
33
+ begin
34
+ ch.queue(name, passive: true)
35
+ true
36
+ rescue MarchHare::NotFound
37
+ false
38
+ ensure
39
+ ch.close rescue nil
40
+ end
41
+ end
42
+
43
+ # MarchHare::Channel has no on_error callback, and neither Channel#reopen
44
+ # nor Session#recover_channel_topology has a `march_hare` counterpart.
45
+ def install_channel_recovery(ch)
46
+ end
47
+
28
48
  def create_channel(n = nil, consumer_pool_size = 1, consumer_pool_abort_on_exception = false)
29
49
  @connection.create_channel(n)
30
50
  end
@@ -34,7 +54,7 @@ module Hutch
34
54
  end
35
55
 
36
56
  def self.new_exchange(ch, exchange_type, exchange_name, exchange_options)
37
- MarchHare::Exchange.new(ch, exchange_name, exchange_options.merge(type: exchange_type))
57
+ MarchHare::Exchange.new(ch, exchange_name, exchange_options.merge(type: exchange_type)).tap(&:declare!)
38
58
  end
39
59
  end
40
60
  end