hutch 0.27.0 → 0.28.0

Sign up to get free protection for your applications and to get access to all the features.
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