hutch 0.27.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 07e4ec8d85eefed7b25ae9bb43fb3c0ff8ffe0a4f24526b986686906aaa0f5cc
4
- data.tar.gz: 94062cdf67c00e9224d3fee9be1fa294ba0e234f8400ec4efc640eaecc07a2df
3
+ metadata.gz: 587ca7c6e7515a0efcf8999a5ad3bfc1ef186a701a758260f68abb528641b7cd
4
+ data.tar.gz: 7f4353919efb856a7ef9af3dc71e7d2024aaa85c1183da3a191bbbf2c22a8492
5
5
  SHA512:
6
- metadata.gz: e5a9ef9a749741aee734246701cde0a99f1c36f02e730eb1e888d2a125dba6e4ed9f9cdf945656ebbf8dcd118775c4717110c4d542b6547df84119baeb29f0a4
7
- data.tar.gz: 8a929dafd7c168256da9c349e4bf60a052456f2d34db4e717e3c46a08e461dac60f95ea6903a76a54bb934fbbcabb62a1c2e24b662469d3e5afd9e459a165b2c
6
+ metadata.gz: 9c00226f20383fc7ec3503197783a48e0d8f153dd7e45ea7d89dda7631f2e527f71b6d15182be677d24818e1ce1cfcf6766765ad6a0e67b753b76cc145b46782
7
+ data.tar.gz: 530af1729a20c10d555bac21529d18c94cb07b1957ce2b6c5386adede9e400c7f7f400491c7c716378fd4fc8d8a955d2537e956dc6d6994aab5fc3cd2f2b5970
@@ -1,3 +1,6 @@
1
+ dist: bionic
2
+ sudo: required
3
+
1
4
  language: ruby
2
5
  cache: bundler
3
6
  before_install:
@@ -5,18 +8,16 @@ before_install:
5
8
  before_script:
6
9
  - "./bin/ci/install_on_debian.sh"
7
10
  - until sudo lsof -i:5672; do echo "Waiting for RabbitMQ to start..."; sleep 1; done
11
+ - "./bin/ci/before_build.sh"
8
12
  matrix:
9
13
  include:
10
14
  - rvm: "2.6.4"
11
15
  - rvm: "2.5.6"
12
16
  - rvm: "2.4.7"
13
17
  - rvm: "2.3.8"
14
- - rvm: "jruby-9.2.8.0"
18
+ - rvm: "jruby-9.2.9.0"
15
19
  - rvm: "ruby-head"
16
20
  allow_failures:
17
21
  rvm:
18
- - "jruby-9.2.8.0"
22
+ - "jruby-9.2.9.0"
19
23
  - ruby-head
20
-
21
- services:
22
- - rabbitmq
@@ -1,7 +1,34 @@
1
1
  ## 0.28.0 (under development)
2
2
 
3
- No chages yet.
3
+ ### Enhancements
4
+
5
+ * Add lazy and quorum options for queues.
6
+
7
+ GitHub issue: [gocardless/hutch#341](https://github.com/gocardless/hutch/pull/341)
8
+
9
+ Contributed by: Arthur Del Esposte
10
+
11
+ * Log level in the message publisher switched to DEBUG.
12
+
13
+ GitHub issue: [gocardless/hutch#343](https://github.com/gocardless/hutch/pull/343)
14
+
15
+ Contributed by: Codruț Constantin Gușoi
16
+
17
+ ### Documentation
18
+
19
+ * Add zeitwerk note to README.
20
+
21
+ GitHub issue: [gocardless/hutch#342](https://github.com/gocardless/hutch/pull/342)
22
+
23
+ Contributed by: Paolo Zaccagnini
24
+
25
+ ### CI
26
+
27
+ * Use jruby-9.2.9.0
28
+
29
+ GitHub issue: [gocardless/hutch#336](https://github.com/gocardless/hutch/pull/336)
4
30
 
31
+ Contributed by: Olle Jonsson
5
32
 
6
33
  ## 0.27.0 (September 9th, 2019)
7
34
 
data/Gemfile CHANGED
@@ -23,7 +23,7 @@ group :development, :test do
23
23
  gem "honeybadger"
24
24
  gem "coveralls", "~> 0.8.15", require: false
25
25
  gem "newrelic_rpm"
26
- gem "airbrake", "~> 9.0"
26
+ gem "airbrake", "~> 10.0"
27
27
  gem "rollbar"
28
28
  end
29
29
 
data/README.md CHANGED
@@ -109,6 +109,28 @@ class FailedPaymentConsumer
109
109
  end
110
110
  ```
111
111
 
112
+ It is possible to set some custom options to consumer's queue explicitly.
113
+ This example sets the consumer's queue as a
114
+ [quorum queue](https://www.rabbitmq.com/quorum-queues.html)
115
+ and to operate in the [lazy mode](https://www.rabbitmq.com/lazy-queues.html).
116
+ The `initial_group_size`
117
+ [argument](https://www.rabbitmq.com/quorum-queues.html#replication-factor) is
118
+ optional.
119
+
120
+ ```ruby
121
+ class FailedPaymentConsumer
122
+ include Hutch::Consumer
123
+ consume 'gc.ps.payment.failed'
124
+ queue_name 'failed_payments'
125
+ lazy_queue
126
+ quorum_queue initial_group_size: 3
127
+
128
+ def process(message)
129
+ mark_payment_as_failed(message[:id])
130
+ end
131
+ end
132
+ ```
133
+
112
134
  You can also set custom arguments per consumer. This example declares a consumer with
113
135
  a maximum length of 10 messages:
114
136
 
@@ -219,6 +241,24 @@ directory of a Rails app, or pass the path to a Rails app in with the
219
241
  the `app/consumers/` directory, to allow them to be auto-loaded when Rails
220
242
  boots.
221
243
 
244
+ If you're using the new Zeitwerk autoloader (enabled by default in Rails 6)
245
+ and the consumers are not loaded in development environment you will need to
246
+ trigger the autoloading in an initializer with
247
+
248
+ ```ruby
249
+ ::Zeitwerk::Loader.eager_load_all
250
+ ```
251
+
252
+ or with something more specific like
253
+
254
+ ```ruby
255
+ autoloader = Rails.autoloaders.main
256
+
257
+ Dir.glob(File.join('app/consumers', '*_consumer.rb')).each do |consumer|
258
+ autoloader.preload(consumer)
259
+ end
260
+ ```
261
+
222
262
  To require files that define consumers manually, simply pass each file as an
223
263
  option to `--require`. Hutch will automatically detect whether you've provided
224
264
  a Rails app or a standard file, and take the appropriate behaviour:
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env sh
2
+
3
+ CTL=${BUNNY_RABBITMQCTL:-"sudo rabbitmqctl"}
4
+ PLUGINS=${BUNNY_RABBITMQ_PLUGINS:-"sudo rabbitmq-plugins"}
5
+
6
+ echo "Will use rabbitmqctl at ${CTL}"
7
+ echo "Will use rabbitmq-plugins at ${PLUGINS}"
8
+
9
+ $PLUGINS enable rabbitmq_management
10
+
11
+ sleep 3
12
+
13
+ # guest:guest has full access to /
14
+ $CTL add_vhost /
15
+ $CTL add_user guest guest
16
+ $CTL set_permissions -p / guest ".*" ".*" ".*"
17
+
18
+ # Reduce retention policy for faster publishing of stats
19
+ $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
20
+ $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
@@ -4,8 +4,8 @@ sudo apt-get install -y wget
4
4
  wget -O - "https://github.com/rabbitmq/signing-keys/releases/download/2.0/rabbitmq-release-signing-key.asc" | sudo apt-key add -
5
5
 
6
6
  sudo tee /etc/apt/sources.list.d/bintray.rabbitmq.list <<EOF
7
- deb https://dl.bintray.com/rabbitmq-erlang/debian xenial erlang
8
- deb https://dl.bintray.com/rabbitmq/debian xenial main
7
+ deb https://dl.bintray.com/rabbitmq-erlang/debian bionic erlang
8
+ deb https://dl.bintray.com/rabbitmq/debian bionic main
9
9
  EOF
10
10
 
11
11
  sudo apt-get update -y
@@ -6,10 +6,10 @@ Gem::Specification.new do |gem|
6
6
  gem.add_runtime_dependency 'march_hare', '>= 3.0.0'
7
7
  else
8
8
  gem.platform = Gem::Platform::RUBY
9
- gem.add_runtime_dependency 'bunny', '>= 2.13', '< 2.15'
9
+ gem.add_runtime_dependency 'bunny', '>= 2.14', '< 2.16'
10
10
  end
11
11
  gem.add_runtime_dependency 'carrot-top', '~> 0.0.7'
12
- gem.add_runtime_dependency 'multi_json', '~> 1.12'
12
+ gem.add_runtime_dependency 'multi_json', '~> 1.14'
13
13
  gem.add_runtime_dependency 'activesupport', '>= 4.2', '< 7'
14
14
 
15
15
  gem.name = 'hutch'
@@ -181,12 +181,15 @@ module Hutch
181
181
  # Return a mapping of queue names to the routing keys they're bound to.
182
182
  def bindings
183
183
  results = Hash.new { |hash, key| hash[key] = [] }
184
- api_client.bindings.each do |binding|
185
- next if binding['destination'] == binding['routing_key']
186
- next unless binding['source'] == @config[:mq_exchange]
187
- next unless binding['vhost'] == @config[:mq_vhost]
184
+
185
+ filtered = api_client.bindings.
186
+ reject { |b| b['destination'] == b['routing_key'] }.
187
+ select { |b| b['source'] == @config[:mq_exchange] && b['vhost'] == @config[:mq_vhost] }
188
+
189
+ filtered.each do |binding|
188
190
  results[binding['destination']] << binding['routing_key']
189
191
  end
192
+
190
193
  results
191
194
  end
192
195
 
@@ -194,8 +197,8 @@ module Hutch
194
197
  def unbind_redundant_bindings(queue, routing_keys)
195
198
  return unless http_api_use_enabled?
196
199
 
197
- bindings.each do |dest, keys|
198
- next unless dest == queue.name
200
+ filtered = bindings.select { |dest, keys| dest == queue.name }
201
+ filtered.each do |dest, keys|
199
202
  keys.reject { |key| routing_keys.include?(key) }.each do |key|
200
203
  logger.debug "removing redundant binding #{queue.name} <--> #{key}"
201
204
  queue.unbind(exchange, routing_key: key)
@@ -33,6 +33,20 @@ module Hutch
33
33
  def load_app
34
34
  # Try to load a Rails app in the current directory
35
35
  load_rails_app('.') if Hutch::Config.autoload_rails
36
+ set_up_code_paths!
37
+
38
+ # Because of the order things are required when we run the Hutch binary
39
+ # in hutch/bin, the Sentry Raven gem gets required **after** the error
40
+ # handlers are set up. Due to this, we never got any Sentry notifications
41
+ # when an error occurred in any of the consumers.
42
+ if defined?(Raven)
43
+ Hutch::Config[:error_handlers] << Hutch::ErrorHandlers::Sentry.new
44
+ end
45
+
46
+ true
47
+ end
48
+
49
+ def set_up_code_paths!
36
50
  Hutch::Config.require_paths.each do |path|
37
51
  # See if each path is a Rails app. If so, try to load it.
38
52
  next if load_rails_app(path)
@@ -51,16 +65,6 @@ module Hutch
51
65
  $LOAD_PATH.pop
52
66
  end
53
67
  end
54
-
55
- # Because of the order things are required when we run the Hutch binary
56
- # in hutch/bin, the Sentry Raven gem gets required **after** the error
57
- # handlers are set up. Due to this, we never got any Sentry notifications
58
- # when an error occurred in any of the consumers.
59
- if defined?(Raven)
60
- Hutch::Config[:error_handlers] << Hutch::ErrorHandlers::Sentry.new
61
- end
62
-
63
- true
64
68
  end
65
69
 
66
70
  def load_rails_app(path)
@@ -29,13 +29,30 @@ module Hutch
29
29
  # wants to subscribe to.
30
30
  def consume(*routing_keys)
31
31
  @routing_keys = self.routing_keys.union(routing_keys)
32
+ @queue_mode = 'default'
33
+ @queue_type = 'classic'
32
34
  end
33
35
 
36
+ attr_reader :queue_mode, :queue_type, :initial_group_size
37
+
34
38
  # Explicitly set the queue name
35
39
  def queue_name(name)
36
40
  @queue_name = name
37
41
  end
38
42
 
43
+ # Explicitly set the queue mode to 'lazy'
44
+ def lazy_queue
45
+ @queue_mode = 'lazy'
46
+ end
47
+
48
+ # Explicitly set the queue type to 'quorum'
49
+ # @param [Hash] options the options params related to quorum queue
50
+ # @option options [Integer] :initial_group_size Initial Replication Factor
51
+ def quorum_queue(options = {})
52
+ @queue_type = 'quorum'
53
+ @initial_group_size = options[:initial_group_size]
54
+ end
55
+
39
56
  # Allow to specify custom arguments that will be passed when creating the queue.
40
57
  def arguments(arguments = {})
41
58
  @arguments = arguments
@@ -58,7 +75,13 @@ module Hutch
58
75
 
59
76
  # Returns consumer custom arguments.
60
77
  def get_arguments
61
- @arguments || {}
78
+ all_arguments = @arguments || {}
79
+ all_arguments['x-queue-mode'] = @queue_mode
80
+ all_arguments['x-queue-type'] = @queue_type
81
+ if @initial_group_size
82
+ all_arguments['x-quorum-initial-group-size'] = @initial_group_size
83
+ end
84
+ all_arguments
62
85
  end
63
86
 
64
87
  # Accessor for the consumer's routing key.
@@ -42,7 +42,7 @@ module Hutch
42
42
  private
43
43
 
44
44
  def log_publication(serializer, payload, routing_key)
45
- logger.info {
45
+ logger.debug {
46
46
  spec =
47
47
  if serializer.binary?
48
48
  "#{payload.bytesize} bytes message"
@@ -1,3 +1,3 @@
1
1
  module Hutch
2
- VERSION = '0.27.0'.freeze
2
+ VERSION = '0.28.0'.freeze
3
3
  end
@@ -28,6 +28,19 @@ describe Hutch::Consumer do
28
28
  ComplexConsumer
29
29
  end
30
30
 
31
+ let(:consumer_with_custom_queue_options) do
32
+ unless defined? ConsumerWithCustomQueueOptions
33
+ class ConsumerWithCustomQueueOptions
34
+ include Hutch::Consumer
35
+ consume 'hutch.test1'
36
+ arguments foo: :bar
37
+ lazy_queue
38
+ quorum_queue
39
+ end
40
+ end
41
+ ConsumerWithCustomQueueOptions
42
+ end
43
+
31
44
  describe 'module inclusion' do
32
45
  it 'registers the class as a consumer' do
33
46
  expect(Hutch).to receive(:register_consumer) do |klass|
@@ -71,6 +84,36 @@ describe Hutch::Consumer do
71
84
  end
72
85
  end
73
86
 
87
+ describe '.lazy_queue' do
88
+ it 'does not use lazy mode by default' do
89
+ expect(simple_consumer.queue_mode).to eq('default')
90
+ end
91
+
92
+ context 'when queue mode has been set explicitly to lazy' do
93
+ it 'sets queue mode to lazy' do
94
+ expect(consumer_with_custom_queue_options.queue_mode).to eq('lazy')
95
+ end
96
+ end
97
+ end
98
+
99
+ describe '.quorum_queue' do
100
+ it 'does not have quorum type by default' do
101
+ expect(simple_consumer.queue_type).to eq('classic')
102
+ end
103
+
104
+ context 'when queue type has been set explicitly to quorum' do
105
+ it 'sets queue type to quorum' do
106
+ expect(consumer_with_custom_queue_options.queue_type).to eq('quorum')
107
+ end
108
+
109
+ it 'accepts initial group size as an option' do
110
+ consumer = simple_consumer
111
+ expect { consumer.quorum_queue(initial_group_size: 3) }
112
+ .to change { consumer.initial_group_size }.to(3)
113
+ end
114
+ end
115
+ end
116
+
74
117
  describe '.arguments' do
75
118
  let(:args) { { foo: :bar} }
76
119
 
@@ -84,13 +127,36 @@ describe Hutch::Consumer do
84
127
  describe '.get_arguments' do
85
128
 
86
129
  context 'when defined' do
87
- it { expect(complex_consumer.get_arguments).to eq(foo: :bar) }
130
+ it { expect(complex_consumer.get_arguments).to include(foo: :bar) }
88
131
  end
89
132
 
90
133
  context 'when not defined' do
91
- it { expect(simple_consumer.get_arguments).to eq({}) }
134
+ it 'has the default values for queue custom options' do
135
+ expect(simple_consumer.get_arguments).to have_key('x-queue-mode')
136
+ .and have_key('x-queue-type')
137
+ end
92
138
  end
93
139
 
140
+ context 'when queue is lazy' do
141
+ it 'has the x-queue-mode argument set to lazy' do
142
+ expect(consumer_with_custom_queue_options.get_arguments['x-queue-mode'])
143
+ .to eq('lazy')
144
+ end
145
+ end
146
+
147
+ context "when queue's type is quorum" do
148
+ let(:arguments) { consumer_with_custom_queue_options.get_arguments }
149
+ it 'has the x-queue-type argument set to quorum' do
150
+ expect(arguments['x-queue-type']).to eq('quorum')
151
+ expect(arguments).to_not have_key('x-quorum-initial-group-size')
152
+ end
153
+
154
+ it 'has the x-quorum-initial-group-size argument set to quorum' do
155
+ consumer_with_custom_queue_options.quorum_queue(initial_group_size: 5)
156
+ expect(arguments['x-queue-type']).to eq('quorum')
157
+ expect(arguments['x-quorum-initial-group-size']).to eq(5)
158
+ end
159
+ end
94
160
  end
95
161
 
96
162
  describe '.get_queue_name' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hutch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.27.0
4
+ version: 0.28.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Harry Marr
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-09-09 00:00:00.000000000 Z
11
+ date: 2020-03-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bunny
@@ -16,20 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '2.13'
19
+ version: '2.14'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: '2.15'
22
+ version: '2.16'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: '2.13'
29
+ version: '2.14'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '2.15'
32
+ version: '2.16'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: carrot-top
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -50,14 +50,14 @@ dependencies:
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '1.12'
53
+ version: '1.14'
54
54
  type: :runtime
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: '1.12'
60
+ version: '1.14'
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: activesupport
63
63
  requirement: !ruby/object:Gem::Requirement
@@ -97,6 +97,7 @@ files:
97
97
  - LICENSE
98
98
  - README.md
99
99
  - Rakefile
100
+ - bin/ci/before_build.sh
100
101
  - bin/ci/install_on_debian.sh
101
102
  - bin/hutch
102
103
  - examples/consumer.rb