sneakers 0.1.1.pre → 1.0.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 +7 -0
- data/.gitignore +3 -0
- data/LICENSE.txt +2 -2
- data/Rakefile +1 -0
- data/examples/max_retry_handler.rb +78 -0
- data/examples/newrelic_metrics_worker.rb +40 -0
- data/lib/sneakers/cli.rb +3 -3
- data/lib/sneakers/concerns/logging.rb +1 -1
- data/lib/sneakers/configuration.rb +47 -0
- data/lib/sneakers/handlers/maxretry.rb +183 -0
- data/lib/sneakers/handlers/oneshot.rb +11 -10
- data/lib/sneakers/publisher.rb +11 -10
- data/lib/sneakers/queue.rb +16 -7
- data/lib/sneakers/runner.rb +4 -4
- data/lib/sneakers/version.rb +1 -1
- data/lib/sneakers/worker.rb +38 -19
- data/lib/sneakers/workergroup.rb +2 -2
- data/lib/sneakers.rb +23 -48
- data/sneakers.gemspec +22 -22
- data/spec/sneakers/concerns/{logging.rb → logging_spec.rb} +1 -1
- data/spec/sneakers/concerns/{metrics.rb → metrics_spec.rb} +0 -0
- data/spec/sneakers/configuration_spec.rb +42 -0
- data/spec/sneakers/publisher_spec.rb +46 -22
- data/spec/sneakers/queue_spec.rb +32 -4
- data/spec/sneakers/sneakers_spec.rb +7 -6
- data/spec/sneakers/worker_handlers_spec.rb +350 -0
- data/spec/sneakers/worker_spec.rb +85 -48
- data/spec/spec_helper.rb +3 -1
- metadata +46 -64
@@ -0,0 +1,350 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'sneakers'
|
3
|
+
require 'timeout'
|
4
|
+
require 'sneakers/handlers/oneshot'
|
5
|
+
require 'sneakers/handlers/maxretry'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
|
9
|
+
# Specific tests of the Handler implementations you can use to deal with job
|
10
|
+
# results. These tests only make sense with a worker that requires acking.
|
11
|
+
|
12
|
+
class HandlerTestWorker
|
13
|
+
include Sneakers::Worker
|
14
|
+
from_queue 'defaults',
|
15
|
+
:ack => true
|
16
|
+
|
17
|
+
def work(msg)
|
18
|
+
if msg.is_a?(StandardError)
|
19
|
+
raise msg
|
20
|
+
elsif msg.is_a?(String)
|
21
|
+
hash = maybe_json(msg)
|
22
|
+
if hash.is_a?(Hash)
|
23
|
+
hash['response'].to_sym
|
24
|
+
else
|
25
|
+
hash
|
26
|
+
end
|
27
|
+
else
|
28
|
+
msg
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def maybe_json(string)
|
33
|
+
JSON.parse(string)
|
34
|
+
rescue
|
35
|
+
string
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class TestPool
|
40
|
+
def process(*args,&block)
|
41
|
+
block.call
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
describe 'Handlers' do
|
47
|
+
let(:channel) { Object.new }
|
48
|
+
let(:queue) { Object.new }
|
49
|
+
let(:worker) { HandlerTestWorker.new(@queue, TestPool.new) }
|
50
|
+
|
51
|
+
before(:each) do
|
52
|
+
Sneakers.configure(:daemonize => true, :log => 'sneakers.log')
|
53
|
+
Sneakers::Worker.configure_logger(Logger.new('/dev/null'))
|
54
|
+
Sneakers::Worker.configure_metrics
|
55
|
+
end
|
56
|
+
|
57
|
+
describe 'Oneshot' do
|
58
|
+
before(:each) do
|
59
|
+
@opts = Object.new
|
60
|
+
@handler = Sneakers::Handlers::Oneshot.new(channel, queue, @opts)
|
61
|
+
|
62
|
+
@header = Object.new
|
63
|
+
stub(@header).delivery_tag { 37 }
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '#do_work' do
|
67
|
+
it 'should work and handle acks' do
|
68
|
+
mock(channel).acknowledge(37, false)
|
69
|
+
|
70
|
+
worker.do_work(@header, nil, :ack, @handler)
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should work and handle rejects' do
|
74
|
+
mock(channel).reject(37, false)
|
75
|
+
|
76
|
+
worker.do_work(@header, nil, :reject, @handler)
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should work and handle requeues' do
|
80
|
+
mock(channel).reject(37, true)
|
81
|
+
|
82
|
+
worker.do_work(@header, nil, :requeue, @handler)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should work and handle user-land timeouts' do
|
86
|
+
mock(channel).reject(37, false)
|
87
|
+
|
88
|
+
worker.do_work(@header, nil, :timeout, @handler)
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'should work and handle user-land error' do
|
92
|
+
mock(channel).reject(37, false)
|
93
|
+
|
94
|
+
worker.do_work(@header, nil, StandardError.new('boom!'), @handler)
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should work and handle noops' do
|
98
|
+
worker.do_work(@header, nil, :wait, @handler)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
describe 'Maxretry' do
|
105
|
+
let(:max_retries) { nil }
|
106
|
+
|
107
|
+
before(:each) do
|
108
|
+
@opts = {
|
109
|
+
:exchange => 'sneakers',
|
110
|
+
:durable => 'true',
|
111
|
+
}.tap do |opts|
|
112
|
+
opts[:retry_max_times] = max_retries unless max_retries.nil?
|
113
|
+
end
|
114
|
+
|
115
|
+
mock(queue).name { 'downloads' }
|
116
|
+
|
117
|
+
@retry_exchange = Object.new
|
118
|
+
@error_exchange = Object.new
|
119
|
+
@requeue_exchange = Object.new
|
120
|
+
|
121
|
+
@retry_queue = Object.new
|
122
|
+
@error_queue = Object.new
|
123
|
+
|
124
|
+
mock(channel).exchange('downloads-retry',
|
125
|
+
:type => 'topic',
|
126
|
+
:durable => 'true').once { @retry_exchange }
|
127
|
+
mock(channel).exchange('downloads-error',
|
128
|
+
:type => 'topic',
|
129
|
+
:durable => 'true').once { @error_exchange }
|
130
|
+
mock(channel).exchange('downloads-retry-requeue',
|
131
|
+
:type => 'topic',
|
132
|
+
:durable => 'true').once { @requeue_exchange }
|
133
|
+
|
134
|
+
mock(channel).queue('downloads-retry',
|
135
|
+
:durable => 'true',
|
136
|
+
:arguments => {
|
137
|
+
:'x-dead-letter-exchange' => 'downloads-retry-requeue',
|
138
|
+
:'x-message-ttl' => 60000
|
139
|
+
}
|
140
|
+
).once { @retry_queue }
|
141
|
+
mock(@retry_queue).bind(@retry_exchange, :routing_key => '#')
|
142
|
+
|
143
|
+
mock(channel).queue('downloads-error',
|
144
|
+
:durable => 'true').once { @error_queue }
|
145
|
+
mock(@error_queue).bind(@error_exchange, :routing_key => '#')
|
146
|
+
|
147
|
+
mock(queue).bind(@requeue_exchange, :routing_key => '#')
|
148
|
+
|
149
|
+
@handler = Sneakers::Handlers::Maxretry.new(channel, queue, @opts)
|
150
|
+
|
151
|
+
@header = Object.new
|
152
|
+
stub(@header).delivery_tag { 37 }
|
153
|
+
|
154
|
+
@props = {}
|
155
|
+
@props_with_x_death = {
|
156
|
+
:headers => {
|
157
|
+
"x-death" => [
|
158
|
+
{
|
159
|
+
"reason" => "expired",
|
160
|
+
"queue" => "downloads-retry",
|
161
|
+
"time" => Time.now,
|
162
|
+
"exchange" => "RawMail-retry",
|
163
|
+
"routing-keys" => ["RawMail"]
|
164
|
+
},
|
165
|
+
{
|
166
|
+
"reason" => "rejected",
|
167
|
+
"queue" => "downloads",
|
168
|
+
"time" => Time.now,
|
169
|
+
"exchange" => "",
|
170
|
+
"routing-keys" => ["RawMail"]
|
171
|
+
}
|
172
|
+
]
|
173
|
+
},
|
174
|
+
:delivery_mode => 1}
|
175
|
+
end
|
176
|
+
|
177
|
+
# it 'allows overriding the retry exchange name'
|
178
|
+
# it 'allows overriding the error exchange name'
|
179
|
+
# it 'allows overriding the retry timeout'
|
180
|
+
|
181
|
+
describe '#do_work' do
|
182
|
+
before do
|
183
|
+
@now = Time.now
|
184
|
+
end
|
185
|
+
|
186
|
+
# Used to stub out the publish method args. Sadly RR doesn't support
|
187
|
+
# this, only proxying existing methods.
|
188
|
+
module MockPublish
|
189
|
+
attr_reader :data, :opts, :called
|
190
|
+
|
191
|
+
def publish(data, opts)
|
192
|
+
@data = data
|
193
|
+
@opts = opts
|
194
|
+
@called = true
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
it 'should work and handle acks' do
|
199
|
+
mock(channel).acknowledge(37, false)
|
200
|
+
|
201
|
+
worker.do_work(@header, @props, :ack, @handler)
|
202
|
+
end
|
203
|
+
|
204
|
+
describe 'rejects' do
|
205
|
+
describe 'more retries ahead' do
|
206
|
+
it 'should work and handle rejects' do
|
207
|
+
mock(channel).reject(37, false)
|
208
|
+
|
209
|
+
worker.do_work(@header, @props_with_x_death, :reject, @handler)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
describe 'no more retries' do
|
214
|
+
let(:max_retries) { 1 }
|
215
|
+
|
216
|
+
it 'sends the rejection to the error queue' do
|
217
|
+
mock(@header).routing_key { '#' }
|
218
|
+
mock(channel).acknowledge(37, false)
|
219
|
+
|
220
|
+
@error_exchange.extend MockPublish
|
221
|
+
worker.do_work(@header, @props_with_x_death, :reject, @handler)
|
222
|
+
@error_exchange.called.must_equal(true)
|
223
|
+
@error_exchange.opts.must_equal({ :routing_key => '#' })
|
224
|
+
data = JSON.parse(@error_exchange.data)
|
225
|
+
data['error'].must_equal('reject')
|
226
|
+
data['num_attempts'].must_equal(2)
|
227
|
+
data['payload'].must_equal(Base64.encode64(:reject.to_s))
|
228
|
+
Time.parse(data['failed_at']).wont_be_nil
|
229
|
+
end
|
230
|
+
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
describe 'requeues' do
|
235
|
+
it 'should work and handle requeues' do
|
236
|
+
mock(channel).reject(37, true)
|
237
|
+
|
238
|
+
worker.do_work(@header, @props_with_x_death, :requeue, @handler)
|
239
|
+
end
|
240
|
+
|
241
|
+
describe 'no more retries left' do
|
242
|
+
let(:max_retries) { 1 }
|
243
|
+
|
244
|
+
it 'continues to reject with requeue' do
|
245
|
+
mock(channel).reject(37, true)
|
246
|
+
|
247
|
+
worker.do_work(@header, @props_with_x_death, :requeue, @handler)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
end
|
252
|
+
|
253
|
+
describe 'timeouts' do
|
254
|
+
describe 'more retries ahead' do
|
255
|
+
it 'should reject the message' do
|
256
|
+
mock(channel).reject(37, false)
|
257
|
+
|
258
|
+
worker.do_work(@header, @props_with_x_death, :timeout, @handler)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
describe 'no more retries left' do
|
263
|
+
let(:max_retries) { 1 }
|
264
|
+
|
265
|
+
it 'sends the rejection to the error queue' do
|
266
|
+
mock(@header).routing_key { '#' }
|
267
|
+
mock(channel).acknowledge(37, false)
|
268
|
+
@error_exchange.extend MockPublish
|
269
|
+
|
270
|
+
worker.do_work(@header, @props_with_x_death, :timeout, @handler)
|
271
|
+
@error_exchange.called.must_equal(true)
|
272
|
+
@error_exchange.opts.must_equal({ :routing_key => '#' })
|
273
|
+
data = JSON.parse(@error_exchange.data)
|
274
|
+
data['error'].must_equal('timeout')
|
275
|
+
data['num_attempts'].must_equal(2)
|
276
|
+
data['payload'].must_equal(Base64.encode64(:timeout.to_s))
|
277
|
+
Time.parse(data['failed_at']).wont_be_nil
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
describe 'exceptions' do
|
283
|
+
describe 'more retries ahead' do
|
284
|
+
it 'should reject the message' do
|
285
|
+
mock(channel).reject(37, false)
|
286
|
+
|
287
|
+
worker.do_work(@header, @props_with_x_death, StandardError.new('boom!'), @handler)
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
describe 'no more retries left' do
|
292
|
+
let(:max_retries) { 1 }
|
293
|
+
|
294
|
+
it 'sends the rejection to the error queue' do
|
295
|
+
mock(@header).routing_key { '#' }
|
296
|
+
mock(channel).acknowledge(37, false)
|
297
|
+
@error_exchange.extend MockPublish
|
298
|
+
|
299
|
+
worker.do_work(@header, @props_with_x_death, StandardError.new('boom!'), @handler)
|
300
|
+
@error_exchange.called.must_equal(true)
|
301
|
+
@error_exchange.opts.must_equal({ :routing_key => '#' })
|
302
|
+
data = JSON.parse(@error_exchange.data)
|
303
|
+
data['error'].must_equal('boom!')
|
304
|
+
data['error_class'].must_equal(StandardError.to_s)
|
305
|
+
data['backtrace'].wont_be_nil
|
306
|
+
data['num_attempts'].must_equal(2)
|
307
|
+
data['payload'].must_equal(Base64.encode64('boom!'))
|
308
|
+
Time.parse(data['failed_at']).wont_be_nil
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
it 'should work and handle user-land error' do
|
314
|
+
mock(channel).reject(37, false)
|
315
|
+
|
316
|
+
worker.do_work(@header, @props, StandardError.new('boom!'), @handler)
|
317
|
+
end
|
318
|
+
|
319
|
+
it 'should work and handle noops' do
|
320
|
+
worker.do_work(@header, @props, :wait, @handler)
|
321
|
+
end
|
322
|
+
|
323
|
+
# Since we encode in json, we want to make sure if the actual payload is
|
324
|
+
# json, then it's something you can get back out.
|
325
|
+
describe 'JSON payloads' do
|
326
|
+
let(:max_retries) { 1 }
|
327
|
+
|
328
|
+
it 'properly encodes the json payload' do
|
329
|
+
mock(@header).routing_key { '#' }
|
330
|
+
mock(channel).acknowledge(37, false)
|
331
|
+
@error_exchange.extend MockPublish
|
332
|
+
|
333
|
+
payload = {
|
334
|
+
data: 'hello',
|
335
|
+
response: :timeout
|
336
|
+
}
|
337
|
+
worker.do_work(@header, @props_with_x_death, payload.to_json, @handler)
|
338
|
+
@error_exchange.called.must_equal(true)
|
339
|
+
@error_exchange.opts.must_equal({ :routing_key => '#' })
|
340
|
+
data = JSON.parse(@error_exchange.data)
|
341
|
+
data['error'].must_equal('timeout')
|
342
|
+
data['num_attempts'].must_equal(2)
|
343
|
+
data['payload'].must_equal(Base64.encode64(payload.to_json))
|
344
|
+
end
|
345
|
+
|
346
|
+
end
|
347
|
+
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
@@ -96,7 +96,7 @@ class WithParamsWorker
|
|
96
96
|
:ack => true,
|
97
97
|
:timeout_job_after => 0.5
|
98
98
|
|
99
|
-
def work_with_params(msg,
|
99
|
+
def work_with_params(msg, delivery_info, metadata)
|
100
100
|
msg
|
101
101
|
end
|
102
102
|
end
|
@@ -108,13 +108,6 @@ class TestPool
|
|
108
108
|
end
|
109
109
|
end
|
110
110
|
|
111
|
-
class TestHandler
|
112
|
-
def acknowledge(tag); end
|
113
|
-
def reject(tag); end
|
114
|
-
def error(tag, err); end
|
115
|
-
def timeout(tag); end
|
116
|
-
end
|
117
|
-
|
118
111
|
def with_test_queuefactory(ctx, ack=true, msg=nil, nowork=false)
|
119
112
|
qf = Object.new
|
120
113
|
q = Object.new
|
@@ -139,7 +132,6 @@ describe Sneakers::Worker do
|
|
139
132
|
stub(@queue).exchange { @exchange }
|
140
133
|
|
141
134
|
Sneakers.configure(:daemonize => true, :log => 'sneakers.log')
|
142
|
-
Sneakers::Worker.configure_logger(Logger.new('/dev/null'))
|
143
135
|
Sneakers::Worker.configure_metrics
|
144
136
|
end
|
145
137
|
|
@@ -167,14 +159,14 @@ describe Sneakers::Worker do
|
|
167
159
|
|
168
160
|
it "should build a queue with correct configuration given defaults" do
|
169
161
|
@defaults_q.name.must_equal('defaults')
|
170
|
-
@defaults_q.opts.must_equal(
|
162
|
+
@defaults_q.opts.to_hash.must_equal(
|
171
163
|
{:runner_config_file=>nil, :metrics=>nil, :daemonize=>true, :start_worker_delay=>0.2, :workers=>4, :log=>"sneakers.log", :pid_path=>"sneakers.pid", :timeout_job_after=>5, :prefetch=>10, :threads=>10, :durable=>true, :ack=>true, :amqp=>"amqp://guest:guest@localhost:5672", :vhost=>"/", :exchange=>"sneakers", :exchange_type=>:direct, :hooks=>{}, :handler=>Sneakers::Handlers::Oneshot, :heartbeat => 2}
|
172
164
|
)
|
173
165
|
end
|
174
166
|
|
175
167
|
it "should build a queue with given configuration" do
|
176
168
|
@dummy_q.name.must_equal('downloads')
|
177
|
-
@dummy_q.opts.must_equal(
|
169
|
+
@dummy_q.opts.to_hash.must_equal(
|
178
170
|
{:runner_config_file=>nil, :metrics=>nil, :daemonize=>true, :start_worker_delay=>0.2, :workers=>4, :log=>"sneakers.log", :pid_path=>"sneakers.pid", :timeout_job_after=>1, :prefetch=>40, :threads=>50, :durable=>false, :ack=>false, :amqp=>"amqp://guest:guest@localhost:5672", :vhost=>"/", :exchange=>"dummy", :exchange_type=>:direct, :hooks=>{}, :handler=>Sneakers::Handlers::Oneshot, :heartbeat =>5}
|
179
171
|
)
|
180
172
|
end
|
@@ -222,9 +214,19 @@ describe Sneakers::Worker do
|
|
222
214
|
w = AcksWorker.new(@queue, TestPool.new)
|
223
215
|
mock(w).work("msg").once{ raise "foo" }
|
224
216
|
handler = Object.new
|
225
|
-
mock(handler).error("tag", anything)
|
226
217
|
header = Object.new
|
227
|
-
|
218
|
+
mock(handler).error(header, nil, "msg", anything)
|
219
|
+
mock(w.logger).error(/unexpected error \[Exception error="foo" error_class=RuntimeError backtrace=.*/)
|
220
|
+
w.do_work(header, nil, "msg", handler)
|
221
|
+
end
|
222
|
+
|
223
|
+
it "should log exceptions from workers" do
|
224
|
+
handler = Object.new
|
225
|
+
header = Object.new
|
226
|
+
w = AcksWorker.new(@queue, TestPool.new)
|
227
|
+
mock(w).work("msg").once{ raise "foo" }
|
228
|
+
mock(w.logger).error(/error="foo" error_class=RuntimeError backtrace=/)
|
229
|
+
mock(handler).error(header, nil, "msg", anything)
|
228
230
|
w.do_work(header, nil, "msg", handler)
|
229
231
|
end
|
230
232
|
|
@@ -233,55 +235,55 @@ describe Sneakers::Worker do
|
|
233
235
|
stub(w).work("msg"){ sleep 10 }
|
234
236
|
|
235
237
|
handler = Object.new
|
236
|
-
mock(handler).timeout("tag")
|
237
|
-
|
238
238
|
header = Object.new
|
239
|
-
|
239
|
+
|
240
|
+
mock(handler).timeout(header, nil, "msg")
|
241
|
+
mock(w.logger).error(/timeout/)
|
240
242
|
|
241
243
|
w.do_work(header, nil, "msg", handler)
|
242
244
|
end
|
243
245
|
|
244
246
|
describe "with ack" do
|
245
247
|
before do
|
246
|
-
@
|
247
|
-
stub(@
|
248
|
+
@delivery_info = Object.new
|
249
|
+
stub(@delivery_info).delivery_tag{ "tag" }
|
248
250
|
|
249
251
|
@worker = AcksWorker.new(@queue, TestPool.new)
|
250
252
|
end
|
251
253
|
|
252
254
|
it "should work and handle acks" do
|
253
255
|
handler = Object.new
|
254
|
-
mock(handler).acknowledge(
|
256
|
+
mock(handler).acknowledge(@delivery_info, nil, :ack)
|
255
257
|
|
256
|
-
@worker.do_work(@
|
258
|
+
@worker.do_work(@delivery_info, nil, :ack, handler)
|
257
259
|
end
|
258
260
|
|
259
261
|
it "should work and handle rejects" do
|
260
262
|
handler = Object.new
|
261
|
-
mock(handler).reject(
|
263
|
+
mock(handler).reject(@delivery_info, nil, :reject)
|
262
264
|
|
263
|
-
@worker.do_work(@
|
265
|
+
@worker.do_work(@delivery_info, nil, :reject, handler)
|
264
266
|
end
|
265
267
|
|
266
268
|
it "should work and handle requeues" do
|
267
269
|
handler = Object.new
|
268
|
-
mock(handler).reject(
|
270
|
+
mock(handler).reject(@delivery_info, nil, :requeue, true)
|
269
271
|
|
270
|
-
@worker.do_work(@
|
272
|
+
@worker.do_work(@delivery_info, nil, :requeue, handler)
|
271
273
|
end
|
272
274
|
|
273
275
|
it "should work and handle user-land timeouts" do
|
274
276
|
handler = Object.new
|
275
|
-
mock(handler).timeout(
|
277
|
+
mock(handler).timeout(@delivery_info, nil, :timeout)
|
276
278
|
|
277
|
-
@worker.do_work(@
|
279
|
+
@worker.do_work(@delivery_info, nil, :timeout, handler)
|
278
280
|
end
|
279
281
|
|
280
282
|
it "should work and handle user-land error" do
|
281
283
|
handler = Object.new
|
282
|
-
mock(handler).error(
|
284
|
+
mock(handler).error(@delivery_info, nil, :error, anything)
|
283
285
|
|
284
|
-
@worker.do_work(@
|
286
|
+
@worker.do_work(@delivery_info, nil, :error, handler)
|
285
287
|
end
|
286
288
|
end
|
287
289
|
|
@@ -304,6 +306,12 @@ describe Sneakers::Worker do
|
|
304
306
|
mock(@exchange).publish('msg', :routing_key => 'target').once
|
305
307
|
w.do_work(nil, nil, 'msg', nil)
|
306
308
|
end
|
309
|
+
|
310
|
+
it 'should be able to publish arbitrary metadata' do
|
311
|
+
w = PublishingWorker.new(@queue, TestPool.new)
|
312
|
+
mock(@exchange).publish('msg', :routing_key => 'target', :expiration => 1).once
|
313
|
+
w.publish 'msg', :to_queue => 'target', :expiration => 1
|
314
|
+
end
|
307
315
|
end
|
308
316
|
|
309
317
|
|
@@ -317,20 +325,39 @@ describe Sneakers::Worker do
|
|
317
325
|
w = LoggingWorker.new(@queue, TestPool.new)
|
318
326
|
w.do_work(nil,nil,'msg',nil)
|
319
327
|
end
|
328
|
+
|
329
|
+
it 'has a helper to constuct log prefix values' do
|
330
|
+
w = DummyWorker.new(@queue, TestPool.new)
|
331
|
+
w.instance_variable_set(:@id, 'worker-id')
|
332
|
+
m = w.log_msg('foo')
|
333
|
+
w.log_msg('foo').must_match(/\[worker-id\]\[#<Thread:.*>\]\[test-queue\]\[\{\}\] foo/)
|
334
|
+
end
|
335
|
+
|
336
|
+
describe '#worker_error' do
|
337
|
+
it 'only logs backtraces if present' do
|
338
|
+
w = DummyWorker.new(@queue, TestPool.new)
|
339
|
+
mock(w.logger).error(/cuz \[Exception error="boom!" error_class=RuntimeError\]/)
|
340
|
+
w.worker_error('cuz', RuntimeError.new('boom!'))
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
320
344
|
end
|
321
345
|
|
322
346
|
|
323
347
|
describe 'Metrics' do
|
324
348
|
before do
|
325
349
|
@handler = Object.new
|
326
|
-
stub(@handler).acknowledge("tag")
|
327
|
-
stub(@handler).reject("tag")
|
328
|
-
stub(@handler).timeout("tag")
|
329
|
-
stub(@handler).error("tag", anything)
|
330
|
-
stub(@handler).noop("tag")
|
331
|
-
|
332
350
|
@header = Object.new
|
333
|
-
|
351
|
+
|
352
|
+
# We don't care how these are called, we're focusing on metrics here.
|
353
|
+
stub(@handler).acknowledge
|
354
|
+
stub(@handler).reject
|
355
|
+
stub(@handler).timeout
|
356
|
+
stub(@handler).error
|
357
|
+
stub(@handler).noop
|
358
|
+
|
359
|
+
@delivery_info = Object.new
|
360
|
+
stub(@delivery_info).delivery_tag { "tag" }
|
334
361
|
|
335
362
|
@w = MetricsWorker.new(@queue, TestPool.new)
|
336
363
|
mock(@w.metrics).increment("work.MetricsWorker.started").once
|
@@ -341,25 +368,37 @@ describe Sneakers::Worker do
|
|
341
368
|
it 'should be able to meter acks' do
|
342
369
|
mock(@w.metrics).increment("foobar").once
|
343
370
|
mock(@w.metrics).increment("work.MetricsWorker.handled.ack").once
|
344
|
-
@w.do_work(@
|
371
|
+
@w.do_work(@delivery_info, nil, :ack, @handler)
|
345
372
|
end
|
346
373
|
|
347
374
|
it 'should be able to meter rejects' do
|
348
375
|
mock(@w.metrics).increment("foobar").once
|
349
376
|
mock(@w.metrics).increment("work.MetricsWorker.handled.reject").once
|
350
|
-
@w.do_work(@header, nil,
|
377
|
+
@w.do_work(@header, nil, :reject, @handler)
|
378
|
+
end
|
379
|
+
|
380
|
+
it 'should be able to meter requeue' do
|
381
|
+
mock(@w.metrics).increment("foobar").once
|
382
|
+
mock(@w.metrics).increment("work.MetricsWorker.handled.requeue").once
|
383
|
+
@w.do_work(@header, nil, :requeue, @handler)
|
351
384
|
end
|
352
385
|
|
353
386
|
it 'should be able to meter errors' do
|
354
387
|
mock(@w.metrics).increment("work.MetricsWorker.handled.error").once
|
355
388
|
mock(@w).work('msg'){ raise :error }
|
356
|
-
@w.do_work(@
|
389
|
+
@w.do_work(@delivery_info, nil, 'msg', @handler)
|
357
390
|
end
|
358
391
|
|
359
392
|
it 'should be able to meter timeouts' do
|
360
393
|
mock(@w.metrics).increment("work.MetricsWorker.handled.timeout").once
|
361
394
|
mock(@w).work('msg'){ sleep 10 }
|
362
|
-
@w.do_work(@
|
395
|
+
@w.do_work(@delivery_info, nil, 'msg', @handler)
|
396
|
+
end
|
397
|
+
|
398
|
+
it 'defaults to noop when no response is specified' do
|
399
|
+
mock(@w.metrics).increment("foobar").once
|
400
|
+
mock(@w.metrics).increment("work.MetricsWorker.handled.noop").once
|
401
|
+
@w.do_work(@header, nil, nil, @handler)
|
363
402
|
end
|
364
403
|
end
|
365
404
|
|
@@ -367,23 +406,21 @@ describe Sneakers::Worker do
|
|
367
406
|
|
368
407
|
describe 'With Params' do
|
369
408
|
before do
|
409
|
+
@props = { :foo => 1 }
|
370
410
|
@handler = Object.new
|
371
|
-
stub(@handler).acknowledge("tag")
|
372
|
-
stub(@handler).reject("tag")
|
373
|
-
stub(@handler).timeout("tag")
|
374
|
-
stub(@handler).error("tag", anything)
|
375
|
-
stub(@handler).noop("tag")
|
376
|
-
|
377
411
|
@header = Object.new
|
378
|
-
|
412
|
+
|
413
|
+
@delivery_info = Object.new
|
414
|
+
|
415
|
+
stub(@handler).noop(@delivery_info, {:foo => 1}, :ack)
|
379
416
|
|
380
417
|
@w = WithParamsWorker.new(@queue, TestPool.new)
|
381
418
|
mock(@w.metrics).timing("work.WithParamsWorker.time").yields.once
|
382
419
|
end
|
383
420
|
|
384
421
|
it 'should call work_with_params and not work' do
|
385
|
-
mock(@w).work_with_params(:ack, @
|
386
|
-
@w.do_work(@
|
422
|
+
mock(@w).work_with_params(:ack, @delivery_info, {:foo => 1}).once
|
423
|
+
@w.do_work(@delivery_info, {:foo => 1 }, :ack, @handler)
|
387
424
|
end
|
388
425
|
end
|
389
426
|
end
|