hutch 0.14.0 → 0.15.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
  SHA1:
3
- metadata.gz: 3c92582d4268b9d9623c60f4b2588d4afc244c17
4
- data.tar.gz: f783601d90dd7031c90c6a6372eb6f2b6b4ef2e4
3
+ metadata.gz: 4edfb298acf9e73fb226a3708e76d7e99ad345e3
4
+ data.tar.gz: d17eb84b63b21b42501570196abe6ef57ca94cde
5
5
  SHA512:
6
- metadata.gz: e40a52fafa102ec87c2df5e885dcffd48a0a6786b08cd4c133122e4da85a2a614aeca75513fbd4f3d464409b6d8189c8585ea4b9d456f893e44b97957ffcba0a
7
- data.tar.gz: 19f8a9dc63bf258f9b0211f49f9b21a509266a1f09eb2d3e3865d3acc2d2eb13a6fdc5d4c682accbaf98ec6d4794f1775d28156723a62e88be2b68c651e4d8bc
6
+ metadata.gz: 99ba07374f0f8bcf407ef537ea0a8342cb4368cf323c0f41deee27a37e1b6cf156dc6a72645bf2ecc6181219a0723c9c6795035d7ac3cc46ad4783f936edd516
7
+ data.tar.gz: 8a6abf7f30f6dcb3c88d968deba3115389b6a043293a75609ff5e441c94da55ac3c31d81dad4049f52710ac12bff59d4643f878ee40bac143d35b3070edf8c84
data/.travis.yml CHANGED
@@ -1,7 +1,8 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.1.0
4
- - 2.0.0
5
- - 1.9.3
3
+ - "2.2"
4
+ - "2.1"
5
+ - "2.0"
6
+ - "1.9.3"
6
7
  services:
7
8
  - rabbitmq
data/CHANGELOG.md CHANGED
@@ -1,4 +1,12 @@
1
- ## 0.14.0 — (unreleased)
1
+ ## 0.15.0 — (unreleased)
2
+
3
+ Allow to set custom arguments per consumers by using the `arguments` setter.
4
+ Arguments are usually used by rabbitmq plugins or to set queue policies. You can
5
+ find a list of supported arguments [here](https://www.rabbitmq.com/extensions.html).
6
+
7
+ Contributed by Pierre-Louis Gottfrois.
8
+
9
+ ## 0.14.0 — Feb 23rd, 2015
2
10
 
3
11
  ### Configurable Socket Timeouts
4
12
 
data/README.md CHANGED
@@ -80,6 +80,19 @@ class FailedPaymentConsumer
80
80
  end
81
81
  ```
82
82
 
83
+ You can also set custom arguments per consumer. This example declares a consumer with
84
+ a maximum length of 10 messages:
85
+
86
+ ```ruby
87
+ class FailedPaymentConsumer
88
+ include Hutch::Consumer
89
+ consume 'gc.ps.payment.failed'
90
+ arguments 'x-max-length' => 10
91
+ end
92
+ ```
93
+
94
+ Custom queue arguments can be found on [this page](https://www.rabbitmq.com/extensions.html).
95
+
83
96
  If you are using Hutch with Rails and want to make Hutch log to the Rails
84
97
  logger rather than `stdout`, add this to `config/initializers/hutch.rb`
85
98
 
@@ -134,8 +147,6 @@ mq_host: broker.yourhost.com
134
147
  Passing a setting as a command-line option will overwrite what's specified
135
148
  in the config file, allowing for easy customization.
136
149
 
137
-
138
-
139
150
  ### Loading Consumers
140
151
 
141
152
  Using Hutch with a Rails app is simple. Either start Hutch in the working
@@ -153,6 +164,20 @@ $ hutch --require path/to/rails-app # loads a rails app
153
164
  $ hutch --require path/to/file.rb # loads a ruby file
154
165
  ```
155
166
 
167
+ ### Stopping Hutch
168
+
169
+ Hutch supports graceful stops. That means that if done correctly, Hutch will wait for your consumer to finish processing before exiting.
170
+
171
+ To gracefully stop your workers, you may send the following signals to your Hutch processes: `INT`, `TERM`, or `QUIT`.
172
+
173
+ ```bash
174
+ kill -SIGINT 123 # or kill -2 123
175
+ kill -SIGTERM 456 # or kill -15 456
176
+ kill -SIGQUIT 789 # or kill -3 789
177
+ ```
178
+
179
+ ![](http://g.recordit.co/wyCdzG9Kh3.gif)
180
+
156
181
  ## Producers
157
182
 
158
183
  Hutch includes a `publish` method for sending messages to Hutch consumers. When
data/lib/hutch/broker.rb CHANGED
@@ -141,11 +141,11 @@ module Hutch
141
141
  end
142
142
 
143
143
  # Create / get a durable queue and apply namespace if it exists.
144
- def queue(name)
144
+ def queue(name, arguments = {})
145
145
  with_bunny_precondition_handler('queue') do
146
146
  namespace = @config[:namespace].to_s.downcase.gsub(/[^-_:\.\w]/, "")
147
147
  name = name.prepend(namespace + ":") unless namespace.empty?
148
- channel.queue(name, durable: true)
148
+ channel.queue(name, durable: true, arguments: arguments)
149
149
  end
150
150
  end
151
151
 
@@ -193,7 +193,12 @@ module Hutch
193
193
  end
194
194
 
195
195
  def stop
196
- @channel.work_pool.kill
196
+ # Enqueue a failing job that kills the consumer loop
197
+ channel_work_pool.shutdown
198
+ # Give `timeout` seconds to jobs that are still being processed
199
+ channel_work_pool.join(@config[:graceful_exit_timeout])
200
+ # If after `timeout` they are still running, they are killed
201
+ channel_work_pool.kill
197
202
  end
198
203
 
199
204
  def requeue(delivery_tag)
@@ -305,7 +310,11 @@ module Hutch
305
310
  end
306
311
 
307
312
  def work_pool_threads
308
- @channel.work_pool.threads || []
313
+ channel_work_pool.threads || []
314
+ end
315
+
316
+ def channel_work_pool
317
+ @channel.work_pool
309
318
  end
310
319
 
311
320
  def generate_id
data/lib/hutch/config.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'hutch/error_handlers/logger'
2
+ require 'erb'
2
3
  require 'logger'
3
4
 
4
5
  module Hutch
@@ -43,7 +44,11 @@ module Hutch
43
44
  connection_timeout: 11,
44
45
  read_timeout: 11,
45
46
  write_timeout: 11,
46
- enable_http_api_use: true
47
+ enable_http_api_use: true,
48
+ # Number of seconds that a running consumer is given
49
+ # to finish its job when gracefully exiting Hutch, before
50
+ # it's killed.
51
+ graceful_exit_timeout: 11,
47
52
  }.merge(params)
48
53
  end
49
54
 
@@ -78,7 +83,7 @@ module Hutch
78
83
  end
79
84
 
80
85
  def self.load_from_file(file)
81
- YAML.load(file).each do |attr, value|
86
+ YAML.load(ERB.new(File.read(file)).result).each do |attr, value|
82
87
  Hutch::Config.send("#{attr}=", value)
83
88
  end
84
89
  end
@@ -32,6 +32,11 @@ module Hutch
32
32
  @queue_name = name
33
33
  end
34
34
 
35
+ # Allow to specify custom arguments that will be passed when creating the queue.
36
+ def arguments(arguments = {})
37
+ @arguments = arguments
38
+ end
39
+
35
40
  # The RabbitMQ queue name for the consumer. This is derived from the
36
41
  # fully-qualified class name. Module separators are replaced with single
37
42
  # colons, camelcased class names are converted to snake case.
@@ -42,6 +47,11 @@ module Hutch
42
47
  queue_name.downcase
43
48
  end
44
49
 
50
+ # Returns consumer custom arguments.
51
+ def get_arguments
52
+ @arguments || {}
53
+ end
54
+
45
55
  # Accessor for the consumer's routing key.
46
56
  def routing_keys
47
57
  @routing_keys ||= Set.new
data/lib/hutch/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Hutch
2
- VERSION = '0.14.0'.freeze
2
+ VERSION = '0.15.0'.freeze
3
3
  end
4
4
 
data/lib/hutch/worker.rb CHANGED
@@ -62,7 +62,7 @@ module Hutch
62
62
  # Bind a consumer's routing keys to its queue, and set up a subscription to
63
63
  # receive messages sent to the queue.
64
64
  def setup_queue(consumer)
65
- queue = @broker.queue(consumer.get_queue_name)
65
+ queue = @broker.queue(consumer.get_queue_name, consumer.get_arguments)
66
66
  @broker.bind_queue(queue, consumer.routing_keys)
67
67
 
68
68
  queue.subscribe(manual_ack: true) do |delivery_info, properties, payload|
@@ -128,14 +128,16 @@ describe Hutch::Broker do
128
128
 
129
129
  describe '#queue' do
130
130
  let(:channel) { double('Channel') }
131
+ let(:arguments) { { foo: :bar } }
131
132
  before { allow(broker).to receive(:channel) { channel } }
132
133
 
133
134
  it 'applies a global namespace' do
134
135
  config[:namespace] = 'mirror-all.service'
135
136
  expect(broker.channel).to receive(:queue) do |*args|
136
- args.first == 'mirror-all.service:test'
137
+ args.first == ''
138
+ args.last == arguments
137
139
  end
138
- broker.queue('test')
140
+ broker.queue('test', arguments)
139
141
  end
140
142
  end
141
143
 
@@ -211,6 +213,25 @@ describe Hutch::Broker do
211
213
  end
212
214
  end
213
215
 
216
+ describe '#stop' do
217
+ let(:thread_1) { double('Thread') }
218
+ let(:thread_2) { double('Thread') }
219
+ let(:work_pool) { double('Bunny::ConsumerWorkPool') }
220
+ let(:config) { { graceful_exit_timeout: 2 } }
221
+
222
+ before do
223
+ allow(broker).to receive(:channel_work_pool).and_return(work_pool)
224
+ end
225
+
226
+ it 'gracefully stops the work pool' do
227
+ expect(work_pool).to receive(:shutdown)
228
+ expect(work_pool).to receive(:join).with(2)
229
+ expect(work_pool).to receive(:kill)
230
+
231
+ broker.stop
232
+ end
233
+ end
234
+
214
235
  describe '#publish' do
215
236
  context 'with a valid connection' do
216
237
  before { broker.set_up_amqp_connection }
@@ -97,4 +97,30 @@ describe Hutch::Config do
97
97
  end
98
98
  end
99
99
  end
100
+
101
+ describe '.load_from_file' do
102
+ let(:host) { 'localhost' }
103
+ let(:username) { 'calvin' }
104
+ let(:file) do
105
+ Tempfile.new('configs.yaml').tap do |t|
106
+ t.write(config_contents)
107
+ t.rewind
108
+ end
109
+ end
110
+
111
+ context 'when using ERb' do
112
+ let(:config_contents) do
113
+ <<-YAML
114
+ mq_host: 'localhost'
115
+ mq_username: '<%= "calvin" %>'
116
+ YAML
117
+ end
118
+
119
+ it 'loads in the config data' do
120
+ Hutch::Config.load_from_file(file)
121
+ expect(Hutch::Config.mq_host).to eq host
122
+ expect(Hutch::Config.mq_username).to eq username
123
+ end
124
+ end
125
+ end
100
126
  end
@@ -22,6 +22,7 @@ describe Hutch::Consumer do
22
22
  class ComplexConsumer
23
23
  include Hutch::Consumer
24
24
  consume 'hutch.test1', 'hutch.test2'
25
+ arguments foo: :bar
25
26
  end
26
27
  end
27
28
  ComplexConsumer
@@ -62,11 +63,36 @@ describe Hutch::Consumer do
62
63
  end
63
64
 
64
65
  describe '.queue_name' do
66
+ let(:queue_name) { 'foo' }
67
+
65
68
  it 'overrides the queue name' do
66
-
69
+ simple_consumer.queue_name(queue_name)
70
+ expect(simple_consumer.get_queue_name).to eq(queue_name)
67
71
  end
68
72
  end
69
73
 
74
+ describe '.arguments' do
75
+ let(:args) { { foo: :bar} }
76
+
77
+ it 'overrides the arguments' do
78
+ simple_consumer.arguments(args)
79
+ expect(simple_consumer.get_arguments).to eq(args)
80
+ end
81
+
82
+ end
83
+
84
+ describe '.get_arguments' do
85
+
86
+ context 'when defined' do
87
+ it { expect(complex_consumer.get_arguments).to eq(foo: :bar) }
88
+ end
89
+
90
+ context 'when not defined' do
91
+ it { expect(simple_consumer.get_arguments).to eq({}) }
92
+ end
93
+
94
+ end
95
+
70
96
  describe '.get_queue_name' do
71
97
 
72
98
  context 'when queue name has been set explicitly' do
@@ -3,7 +3,7 @@ require 'hutch/worker'
3
3
 
4
4
  describe Hutch::Worker do
5
5
  let(:consumer) { double('Consumer', routing_keys: %w( a b c ),
6
- get_queue_name: 'consumer') }
6
+ get_queue_name: 'consumer', get_arguments: {}) }
7
7
  let(:consumers) { [consumer, double('Consumer')] }
8
8
  let(:broker) { Hutch::Broker.new }
9
9
  subject(:worker) { Hutch::Worker.new(broker, consumers) }
@@ -23,7 +23,7 @@ describe Hutch::Worker do
23
23
  before { allow(broker).to receive_messages(queue: queue, bind_queue: nil) }
24
24
 
25
25
  it 'creates a queue' do
26
- expect(broker).to receive(:queue).with(consumer.get_queue_name).and_return(queue)
26
+ expect(broker).to receive(:queue).with(consumer.get_queue_name, consumer.get_arguments).and_return(queue)
27
27
  worker.setup_queue(consumer)
28
28
  end
29
29
 
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.14.0
4
+ version: 0.15.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: 2015-02-23 00:00:00.000000000 Z
11
+ date: 2015-05-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bunny