backburner-allq 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 +17 -0
- data/.travis.yml +29 -0
- data/CHANGELOG.md +133 -0
- data/CONTRIBUTING.md +37 -0
- data/Gemfile +4 -0
- data/HOOKS.md +99 -0
- data/LICENSE +22 -0
- data/README.md +658 -0
- data/Rakefile +17 -0
- data/TODO +4 -0
- data/backburner-allq.gemspec +26 -0
- data/bin/backburner +7 -0
- data/circle.yml +3 -0
- data/deploy.sh +3 -0
- data/examples/custom.rb +25 -0
- data/examples/demo.rb +60 -0
- data/examples/god.rb +46 -0
- data/examples/hooked.rb +87 -0
- data/examples/retried.rb +31 -0
- data/examples/simple.rb +43 -0
- data/examples/stress.rb +31 -0
- data/lib/backburner.rb +75 -0
- data/lib/backburner/allq_wrapper.rb +317 -0
- data/lib/backburner/async_proxy.rb +25 -0
- data/lib/backburner/cli.rb +53 -0
- data/lib/backburner/configuration.rb +48 -0
- data/lib/backburner/connection.rb +157 -0
- data/lib/backburner/helpers.rb +193 -0
- data/lib/backburner/hooks.rb +53 -0
- data/lib/backburner/job.rb +118 -0
- data/lib/backburner/logger.rb +53 -0
- data/lib/backburner/performable.rb +95 -0
- data/lib/backburner/queue.rb +145 -0
- data/lib/backburner/tasks.rb +54 -0
- data/lib/backburner/version.rb +3 -0
- data/lib/backburner/worker.rb +221 -0
- data/lib/backburner/workers/forking.rb +52 -0
- data/lib/backburner/workers/simple.rb +29 -0
- data/lib/backburner/workers/threading.rb +163 -0
- data/lib/backburner/workers/threads_on_fork.rb +263 -0
- data/test/async_proxy_test.rb +36 -0
- data/test/back_burner_test.rb +88 -0
- data/test/connection_test.rb +179 -0
- data/test/fixtures/hooked.rb +122 -0
- data/test/fixtures/test_fork_jobs.rb +72 -0
- data/test/fixtures/test_forking_jobs.rb +56 -0
- data/test/fixtures/test_jobs.rb +87 -0
- data/test/fixtures/test_queue_settings.rb +14 -0
- data/test/helpers/templogger.rb +22 -0
- data/test/helpers_test.rb +278 -0
- data/test/hooks_test.rb +112 -0
- data/test/job_test.rb +185 -0
- data/test/logger_test.rb +44 -0
- data/test/performable_test.rb +88 -0
- data/test/queue_test.rb +69 -0
- data/test/test_helper.rb +128 -0
- data/test/worker_test.rb +157 -0
- data/test/workers/forking_worker_test.rb +181 -0
- data/test/workers/simple_worker_test.rb +350 -0
- data/test/workers/threading_worker_test.rb +104 -0
- data/test/workers/threads_on_fork_worker_test.rb +484 -0
- metadata +217 -0
@@ -0,0 +1,350 @@
|
|
1
|
+
require File.expand_path('../../test_helper', __FILE__)
|
2
|
+
require File.expand_path('../../fixtures/test_jobs', __FILE__)
|
3
|
+
require File.expand_path('../../fixtures/hooked', __FILE__)
|
4
|
+
|
5
|
+
describe "Backburner::Workers::Simple module" do
|
6
|
+
before do
|
7
|
+
Backburner.default_queues.clear
|
8
|
+
@worker_class = Backburner::Workers::Simple
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "for prepare method" do
|
12
|
+
it "should make tube names array always unique to avoid duplication" do
|
13
|
+
worker = @worker_class.new(["foo", "demo.test.foo"])
|
14
|
+
capture_stdout { worker.prepare }
|
15
|
+
assert_equal ["demo.test.foo"], worker.tube_names
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should watch specified tubes" do
|
19
|
+
worker = @worker_class.new(["foo", "bar"])
|
20
|
+
out = capture_stdout { worker.prepare }
|
21
|
+
assert_equal ["demo.test.foo", "demo.test.bar"], worker.tube_names
|
22
|
+
assert_same_elements ["demo.test.foo", "demo.test.bar"], worker.connection.tubes.watched.map(&:name)
|
23
|
+
assert_match(/demo\.test\.foo/, out)
|
24
|
+
end # multiple
|
25
|
+
|
26
|
+
it "should watch single tube" do
|
27
|
+
worker = @worker_class.new("foo")
|
28
|
+
out = capture_stdout { worker.prepare }
|
29
|
+
assert_equal ["demo.test.foo"], worker.tube_names
|
30
|
+
assert_same_elements ["demo.test.foo"], worker.connection.tubes.watched.map(&:name)
|
31
|
+
assert_match(/demo\.test\.foo/, out)
|
32
|
+
end # single
|
33
|
+
|
34
|
+
it "should respect default_queues settings" do
|
35
|
+
Backburner.default_queues.concat(["foo", "bar"])
|
36
|
+
worker = @worker_class.new
|
37
|
+
out = capture_stdout { worker.prepare }
|
38
|
+
assert_equal ["demo.test.foo", "demo.test.bar"], worker.tube_names
|
39
|
+
assert_same_elements ["demo.test.foo", "demo.test.bar"], worker.connection.tubes.watched.map(&:name)
|
40
|
+
assert_match(/demo\.test\.foo/, out)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should assign based on all tubes" do
|
44
|
+
@worker_class.any_instance.expects(:all_existing_queues).once.returns("bar")
|
45
|
+
worker = @worker_class.new
|
46
|
+
out = capture_stdout { worker.prepare }
|
47
|
+
assert_equal ["demo.test.bar"], worker.tube_names
|
48
|
+
assert_same_elements ["demo.test.bar"], worker.connection.tubes.watched.map(&:name)
|
49
|
+
assert_match(/demo\.test\.bar/, out)
|
50
|
+
end # all assign
|
51
|
+
|
52
|
+
it "should properly retrieve all tubes" do
|
53
|
+
worker = @worker_class.new
|
54
|
+
out = capture_stdout { worker.prepare }
|
55
|
+
assert_contains worker.tube_names, "demo.test.backburner-jobs"
|
56
|
+
assert_contains worker.connection.tubes.watched.map(&:name), "demo.test.backburner-jobs"
|
57
|
+
assert_match(/demo\.test\.backburner-jobs/, out)
|
58
|
+
end # all read
|
59
|
+
end # prepare
|
60
|
+
|
61
|
+
describe "for work_one_job method" do
|
62
|
+
before do
|
63
|
+
$worker_test_count = 0
|
64
|
+
$worker_success = false
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should work a plain enqueued job" do
|
68
|
+
clear_jobs!("foo.bar")
|
69
|
+
@worker_class.enqueue TestPlainJob, [1, 2], :queue => "foo.bar"
|
70
|
+
silenced(2) do
|
71
|
+
worker = @worker_class.new('foo.bar')
|
72
|
+
worker.prepare
|
73
|
+
worker.work_one_job
|
74
|
+
end
|
75
|
+
assert_equal 4, $worker_test_count
|
76
|
+
end # plain enqueue
|
77
|
+
|
78
|
+
it "should work an enqueued job" do
|
79
|
+
clear_jobs!("foo.bar")
|
80
|
+
@worker_class.enqueue TestJob, [1, 2], :queue => "foo.bar"
|
81
|
+
silenced(2) do
|
82
|
+
worker = @worker_class.new('foo.bar')
|
83
|
+
worker.prepare
|
84
|
+
worker.work_one_job
|
85
|
+
end
|
86
|
+
assert_equal 3, $worker_test_count
|
87
|
+
end # enqueue
|
88
|
+
|
89
|
+
it "should fail quietly if there's an argument error" do
|
90
|
+
clear_jobs!("foo.bar")
|
91
|
+
@worker_class.enqueue TestJob, ["bam", "foo", "bar"], :queue => "foo.bar"
|
92
|
+
out = silenced(2) do
|
93
|
+
worker = @worker_class.new('foo.bar')
|
94
|
+
worker.prepare
|
95
|
+
worker.work_one_job
|
96
|
+
end
|
97
|
+
assert_match(/Exception ArgumentError/, out)
|
98
|
+
assert_equal 0, $worker_test_count
|
99
|
+
end # fail, argument
|
100
|
+
|
101
|
+
it "should work an enqueued failing job" do
|
102
|
+
# NB: The #bury expectation below leaves the job in the queue (as reserved!)
|
103
|
+
# since bury is never actually called on the task. Therefore, clear_jobs!()
|
104
|
+
# can't remove it which can break a lot of things depending on the order the
|
105
|
+
# tests are run. So we ensure that it's using a unique queue name. Mocha
|
106
|
+
# lacks expectations with proxies (where we could actually call bury)
|
107
|
+
clear_jobs!('foo.bar.failed')
|
108
|
+
@worker_class.enqueue TestFailJob, [1, 2], :queue => 'foo.bar.failed'
|
109
|
+
Backburner::Job.any_instance.expects(:bury).once
|
110
|
+
out = silenced(2) do
|
111
|
+
worker = @worker_class.new('foo.bar.failed')
|
112
|
+
worker.prepare
|
113
|
+
worker.work_one_job
|
114
|
+
end
|
115
|
+
assert_match(/Exception RuntimeError/, out)
|
116
|
+
assert_equal 0, $worker_test_count
|
117
|
+
end # fail, runtime error
|
118
|
+
|
119
|
+
it "should work an invalid job parsed" do
|
120
|
+
Beaneater::Tubes.any_instance.expects(:reserve).returns(stub(:body => "{%$^}", :bury => true))
|
121
|
+
out = silenced(2) do
|
122
|
+
worker = @worker_class.new('foo.bar')
|
123
|
+
worker.prepare
|
124
|
+
worker.work_one_job
|
125
|
+
end
|
126
|
+
assert_match(/Exception Backburner::Job::JobFormatInvalid/, out)
|
127
|
+
assert_equal 0, $worker_test_count
|
128
|
+
end # fail, runtime error
|
129
|
+
|
130
|
+
it "should work for an async job" do
|
131
|
+
clear_jobs!('foo.bar')
|
132
|
+
TestAsyncJob.async(:queue => 'foo.bar').foo(3, 5)
|
133
|
+
silenced(2) do
|
134
|
+
worker = @worker_class.new('foo.bar')
|
135
|
+
worker.prepare
|
136
|
+
worker.work_one_job
|
137
|
+
end
|
138
|
+
assert_equal 15, $worker_test_count
|
139
|
+
end # async
|
140
|
+
|
141
|
+
it "should support retrying jobs and burying" do
|
142
|
+
clear_jobs!('foo.bar')
|
143
|
+
Backburner.configure { |config| config.max_job_retries = 1; config.retry_delay = 0 }
|
144
|
+
@worker_class.enqueue TestRetryJob, ["bam", "foo"], :queue => 'foo.bar'
|
145
|
+
out = []
|
146
|
+
2.times do
|
147
|
+
out << silenced(2) do
|
148
|
+
worker = @worker_class.new('foo.bar')
|
149
|
+
worker.prepare
|
150
|
+
worker.work_one_job
|
151
|
+
end
|
152
|
+
end
|
153
|
+
assert_match(/attempt 1 of 2, retrying/, out.first)
|
154
|
+
assert_match(/Finished TestRetryJob/m, out.last)
|
155
|
+
assert_match(/attempt 2 of 2, burying/m, out.last)
|
156
|
+
assert_equal 2, $worker_test_count
|
157
|
+
assert_equal false, $worker_success
|
158
|
+
end # retry, bury
|
159
|
+
|
160
|
+
it "should support retrying jobs and succeeds" do
|
161
|
+
clear_jobs!('foo.bar')
|
162
|
+
Backburner.configure { |config| config.max_job_retries = 2; config.retry_delay = 0 }
|
163
|
+
@worker_class.enqueue TestRetryJob, ["bam", "foo"], :queue => 'foo.bar'
|
164
|
+
out = []
|
165
|
+
3.times do
|
166
|
+
out << silenced(2) do
|
167
|
+
worker = @worker_class.new('foo.bar')
|
168
|
+
worker.prepare
|
169
|
+
worker.work_one_job
|
170
|
+
end
|
171
|
+
end
|
172
|
+
assert_match(/attempt 1 of 3, retrying/, out.first)
|
173
|
+
assert_match(/attempt 2 of 3, retrying/, out[1])
|
174
|
+
assert_match(/Completed TestRetryJob/m, out.last)
|
175
|
+
refute_match(/failed/, out.last)
|
176
|
+
assert_equal 3, $worker_test_count
|
177
|
+
assert_equal true, $worker_success
|
178
|
+
end # retrying, succeeds
|
179
|
+
|
180
|
+
it "should back off retries exponentially" do
|
181
|
+
max_job_retries = 3
|
182
|
+
clear_jobs!('foo.bar')
|
183
|
+
Backburner.configure do |config|
|
184
|
+
config.max_job_retries = max_job_retries
|
185
|
+
config.retry_delay = 0
|
186
|
+
#config.retry_delay_proc = lambda { |min_retry_delay, num_retries| min_retry_delay + (num_retries ** 3) } # default retry_delay_proc
|
187
|
+
end
|
188
|
+
@worker_class.enqueue TestConfigurableRetryJob, [max_job_retries], :queue => 'foo.bar'
|
189
|
+
out = []
|
190
|
+
(max_job_retries + 1).times do
|
191
|
+
out << silenced(10) do
|
192
|
+
worker = @worker_class.new('foo.bar')
|
193
|
+
worker.prepare
|
194
|
+
worker.work_one_job
|
195
|
+
end
|
196
|
+
end
|
197
|
+
assert_match(/attempt 1 of 4, retrying in 0/, out.first)
|
198
|
+
assert_match(/attempt 2 of 4, retrying in 1/, out[1])
|
199
|
+
assert_match(/attempt 3 of 4, retrying in 8/, out[2])
|
200
|
+
assert_match(/Completed TestConfigurableRetryJob/m, out.last)
|
201
|
+
refute_match(/failed/, out.last)
|
202
|
+
assert_equal 4, $worker_test_count
|
203
|
+
assert_equal true, $worker_success
|
204
|
+
end
|
205
|
+
|
206
|
+
it "should allow configurable back off retry delays" do
|
207
|
+
max_job_retries = 3
|
208
|
+
clear_jobs!('foo.bar')
|
209
|
+
Backburner.configure do |config|
|
210
|
+
config.max_job_retries = max_job_retries
|
211
|
+
config.retry_delay = 0
|
212
|
+
config.retry_delay_proc = lambda { |min_retry_delay, num_retries| min_retry_delay + (num_retries ** 2) }
|
213
|
+
end
|
214
|
+
@worker_class.enqueue TestConfigurableRetryJob, [max_job_retries], :queue => 'foo.bar'
|
215
|
+
out = []
|
216
|
+
(max_job_retries + 1).times do
|
217
|
+
out << silenced(5) do
|
218
|
+
worker = @worker_class.new('foo.bar')
|
219
|
+
worker.prepare
|
220
|
+
worker.work_one_job
|
221
|
+
end
|
222
|
+
end
|
223
|
+
assert_match(/attempt 1 of 4, retrying in 0/, out.first)
|
224
|
+
assert_match(/attempt 2 of 4, retrying in 1/, out[1])
|
225
|
+
assert_match(/attempt 3 of 4, retrying in 4/, out[2])
|
226
|
+
assert_match(/Completed TestConfigurableRetryJob/m, out.last)
|
227
|
+
refute_match(/failed/, out.last)
|
228
|
+
assert_equal 4, $worker_test_count
|
229
|
+
assert_equal true, $worker_success
|
230
|
+
end
|
231
|
+
|
232
|
+
it "should allow queue override of retries" do
|
233
|
+
max_job_retries = TestRetryWithQueueOverridesJob.queue_max_job_retries
|
234
|
+
clear_jobs!('foo.bar')
|
235
|
+
Backburner.configure do |config|
|
236
|
+
# Config should be overridden by queue overrides
|
237
|
+
config.max_job_retries = 20
|
238
|
+
config.retry_delay = 60
|
239
|
+
#config.retry_delay_proc = lambda { |min_retry_delay, num_retries| min_retry_delay + (num_retries ** 3) } # default retry_delay_proc
|
240
|
+
end
|
241
|
+
@worker_class.enqueue TestRetryWithQueueOverridesJob, [max_job_retries], :queue => 'foo.bar'
|
242
|
+
out = []
|
243
|
+
(max_job_retries + 1).times do
|
244
|
+
out << silenced(5) do
|
245
|
+
worker = @worker_class.new('foo.bar')
|
246
|
+
worker.prepare
|
247
|
+
worker.work_one_job
|
248
|
+
end
|
249
|
+
end
|
250
|
+
assert_match(/attempt 1 of 4, retrying in 0/, out.first)
|
251
|
+
assert_match(/attempt 2 of 4, retrying in 1/, out[1])
|
252
|
+
assert_match(/attempt 3 of 4, retrying in 4/, out[2])
|
253
|
+
assert_match(/Completed TestRetryWithQueueOverridesJob/m, out.last)
|
254
|
+
refute_match(/failed/, out.last)
|
255
|
+
assert_equal 4, $worker_test_count
|
256
|
+
assert_equal true, $worker_success
|
257
|
+
end
|
258
|
+
|
259
|
+
it "should support event hooks without retry" do
|
260
|
+
$hooked_fail_count = 0
|
261
|
+
clear_jobs!('foo.bar.events')
|
262
|
+
out = silenced(2) do
|
263
|
+
HookedObjectSuccess.async(:queue => 'foo.bar.events').foo(5)
|
264
|
+
worker = @worker_class.new('foo.bar.events')
|
265
|
+
worker.prepare
|
266
|
+
worker.work_one_job
|
267
|
+
end
|
268
|
+
assert_match(/before_enqueue.*after_enqueue.*Working 1 queues/m, out)
|
269
|
+
assert_match(/!!before_enqueue_bar!! \[nil, :foo, 5\]/, out)
|
270
|
+
assert_match(/!!after_enqueue_bar!! \[nil, :foo, 5\]/, out)
|
271
|
+
assert_match(/!!before_perform_foo!! \[nil, "foo", 5\]/, out)
|
272
|
+
assert_match(/!!BEGIN around_perform_bar!! \[nil, "foo", 5\]/, out)
|
273
|
+
assert_match(/!!BEGIN around_perform_cat!! \[nil, "foo", 5\]/, out)
|
274
|
+
assert_match(/!!on_failure_foo!!.*HookFailError/, out)
|
275
|
+
assert_match(/!!on_bury_foo!! \[nil, "foo", 5\]/, out)
|
276
|
+
assert_match(/attempt 1 of 1, burying/, out)
|
277
|
+
end # event hooks, no retry
|
278
|
+
|
279
|
+
it "should support event hooks with retry" do
|
280
|
+
$hooked_fail_count = 0
|
281
|
+
clear_jobs!('foo.bar.events.retry')
|
282
|
+
Backburner.configure { |config| config.max_job_retries = 1; config.retry_delay = 0 }
|
283
|
+
out = silenced(2) do
|
284
|
+
HookedObjectSuccess.async(:queue => 'foo.bar.events.retry').foo(5)
|
285
|
+
worker = @worker_class.new('foo.bar.events.retry')
|
286
|
+
worker.prepare
|
287
|
+
2.times do
|
288
|
+
worker.work_one_job
|
289
|
+
end
|
290
|
+
end
|
291
|
+
assert_match(/before_enqueue.*after_enqueue.*Working 1 queues/m, out)
|
292
|
+
assert_match(/!!before_enqueue_bar!! \[nil, :foo, 5\]/, out)
|
293
|
+
assert_match(/!!after_enqueue_bar!! \[nil, :foo, 5\]/, out)
|
294
|
+
assert_match(/!!before_perform_foo!! \[nil, "foo", 5\]/, out)
|
295
|
+
assert_match(/!!BEGIN around_perform_bar!! \[nil, "foo", 5\]/, out)
|
296
|
+
assert_match(/!!BEGIN around_perform_cat!! \[nil, "foo", 5\]/, out)
|
297
|
+
assert_match(/!!on_failure_foo!!.*HookFailError/, out)
|
298
|
+
assert_match(/!!on_failure_foo!!.*retrying.*around_perform_bar.*around_perform_cat/m, out)
|
299
|
+
assert_match(/!!on_retry_foo!! 1 0 \[nil, "foo", 5\]/, out)
|
300
|
+
assert_match(/attempt 1 of 2, retrying/, out)
|
301
|
+
assert_match(/!!before_perform_foo!! \[nil, "foo", 5\]/, out)
|
302
|
+
assert_match(/!!END around_perform_bar!! \[nil, "foo", 5\]/, out)
|
303
|
+
assert_match(/!!END around_perform_cat!! \[nil, "foo", 5\]/, out)
|
304
|
+
assert_match(/!!after_perform_foo!! \[nil, "foo", 5\]/, out)
|
305
|
+
assert_match(/Finished HookedObjectSuccess/, out)
|
306
|
+
end # event hooks, with retry
|
307
|
+
|
308
|
+
it "should support event hooks with stopping enqueue" do
|
309
|
+
$hooked_fail_count = 0
|
310
|
+
worker = @worker_class.new('foo.bar.events.retry2')
|
311
|
+
clear_jobs!('foo.bar.events.retry2')
|
312
|
+
silenced(2) do
|
313
|
+
HookedObjectBeforeEnqueueFail.async(:queue => 'foo.bar.events.retry2').foo(5)
|
314
|
+
end
|
315
|
+
expanded_tube = [Backburner.configuration.tube_namespace, 'foo.bar.events.retry2'].join(".")
|
316
|
+
assert_nil worker.connection.tubes[expanded_tube].peek(:ready)
|
317
|
+
end # stopping enqueue
|
318
|
+
|
319
|
+
it "should support event hooks with stopping perform" do
|
320
|
+
$hooked_fail_count = 0
|
321
|
+
clear_jobs!('foo.bar.events.retry3')
|
322
|
+
[Backburner.configuration.tube_namespace, 'foo.bar.events.retry3'].join(".")
|
323
|
+
out = silenced(2) do
|
324
|
+
HookedObjectBeforePerformFail.async(:queue => 'foo.bar.events.retry3').foo(10)
|
325
|
+
worker = @worker_class.new('foo.bar.events.retry3')
|
326
|
+
worker.prepare
|
327
|
+
worker.work_one_job
|
328
|
+
end
|
329
|
+
assert_match(/!!before_perform_foo!! \[nil, "foo", 10\]/, out)
|
330
|
+
assert_match(/before_perform_foo.*Completed/m, out)
|
331
|
+
refute_match(/Fail ran!!/, out)
|
332
|
+
refute_match(/HookFailError/, out)
|
333
|
+
end # stopping perform
|
334
|
+
|
335
|
+
it "should use the connection given as an argument" do
|
336
|
+
worker = @worker_class.new('foo.bar')
|
337
|
+
connection = mock('connection')
|
338
|
+
worker.expects(:reserve_job).with(connection).returns(stub_everything('job'))
|
339
|
+
capture_stdout { worker.work_one_job(connection) }
|
340
|
+
end
|
341
|
+
|
342
|
+
after do
|
343
|
+
Backburner.configure do |config|
|
344
|
+
config.max_job_retries = 0
|
345
|
+
config.retry_delay = 5
|
346
|
+
config.retry_delay_proc = lambda { |min_retry_delay, num_retries| min_retry_delay + (num_retries ** 3) }
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end # work_one_job
|
350
|
+
end # Worker
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require File.expand_path('../../test_helper', __FILE__)
|
2
|
+
require File.expand_path('../../fixtures/test_jobs', __FILE__)
|
3
|
+
require File.expand_path('../../fixtures/hooked', __FILE__)
|
4
|
+
|
5
|
+
describe "Backburner::Workers::Threading worker" do
|
6
|
+
before do
|
7
|
+
Backburner.default_queues.clear
|
8
|
+
@worker_class = Backburner::Workers::Threading
|
9
|
+
@worker_class.shutdown_timeout = 2
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "for prepare method" do
|
13
|
+
it "should make tube names array always unique to avoid duplication" do
|
14
|
+
worker = @worker_class.new(["foo", "demo.test.foo"])
|
15
|
+
capture_stdout { worker.prepare }
|
16
|
+
assert_equal ["demo.test.foo"], worker.tube_names
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'creates a thread pool per queue' do
|
20
|
+
worker = @worker_class.new(%w(foo bar))
|
21
|
+
capture_stdout { worker.prepare }
|
22
|
+
assert_equal 2, worker.instance_variable_get("@thread_pools").keys.size
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'uses Concurrent.processor_count if no custom thread count is provided' do
|
26
|
+
worker = @worker_class.new("foo")
|
27
|
+
capture_stdout { worker.prepare }
|
28
|
+
assert_equal ::Concurrent.processor_count, worker.instance_variable_get("@thread_pools")["demo.test.foo"].max_length
|
29
|
+
end
|
30
|
+
end # prepare
|
31
|
+
|
32
|
+
describe "for process_tube_names method" do
|
33
|
+
it "should interpret the job_name:threads_limit format" do
|
34
|
+
worker = @worker_class.new(["foo:4"])
|
35
|
+
assert_equal ["foo"], worker.tube_names
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should interpret correctly even if missing values" do
|
39
|
+
tubes = %W(foo1:2 foo2)
|
40
|
+
worker = @worker_class.new(tubes)
|
41
|
+
assert_equal %W(foo1 foo2), worker.tube_names
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should store interpreted values correctly" do
|
45
|
+
tubes = %W(foo1 foo2:2)
|
46
|
+
worker = @worker_class.new(tubes)
|
47
|
+
assert_equal({
|
48
|
+
"demo.test.foo1" => { :threads => nil },
|
49
|
+
"demo.test.foo2" => { :threads => 2 }
|
50
|
+
}, worker.instance_variable_get("@tubes_data"))
|
51
|
+
end
|
52
|
+
end # process_tube_names
|
53
|
+
|
54
|
+
describe 'working a queue' do
|
55
|
+
before do
|
56
|
+
@worker = @worker_class.new(["foo:3"])
|
57
|
+
capture_stdout { @worker.prepare }
|
58
|
+
$worker_test_count = 0
|
59
|
+
$worker_success = false
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'runs work_on_job per thread' do
|
63
|
+
clear_jobs!("foo")
|
64
|
+
job_count=10
|
65
|
+
# TestJob adds the given arguments together and then to $worker_test_count
|
66
|
+
job_count.times { @worker_class.enqueue TestJob, [1, 0], :queue => "foo" }
|
67
|
+
capture_stdout do
|
68
|
+
@worker.start(false) # don't wait for shutdown
|
69
|
+
sleep 0.5 # Wait for threads to do their work
|
70
|
+
end
|
71
|
+
assert_equal job_count, $worker_test_count
|
72
|
+
end
|
73
|
+
end # working a queue
|
74
|
+
|
75
|
+
describe 'shutting down' do
|
76
|
+
before do
|
77
|
+
@thread_count = 3
|
78
|
+
@worker = @worker_class.new(["threaded-shutdown:#{@thread_count}"])
|
79
|
+
@worker.exit_on_shutdown = false
|
80
|
+
$worker_test_count = 0
|
81
|
+
clear_jobs!("threaded-shutdown")
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'gracefully exits and completes all in-flight jobs' do
|
85
|
+
6.times { @worker_class.enqueue TestSlowJob, [1, 0], :queue => "threaded-shutdown" }
|
86
|
+
Thread.new { sleep 0.1; @worker.self_write.puts("TERM") }
|
87
|
+
capture_stdout do
|
88
|
+
@worker.start
|
89
|
+
end
|
90
|
+
|
91
|
+
assert_equal @thread_count, $worker_test_count
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'forces an exit when a job is stuck' do
|
95
|
+
6.times { @worker_class.enqueue TestStuckJob, [1, 0], :queue => "threaded-shutdown" }
|
96
|
+
Thread.new { sleep 0.1; @worker.self_write.puts("TERM") }
|
97
|
+
capture_stdout do
|
98
|
+
@worker.start
|
99
|
+
end
|
100
|
+
|
101
|
+
assert_equal 0, $worker_test_count
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|