backburner 1.6.1 → 1.8.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 +4 -4
- data/.github/workflows/ci.yml +45 -0
- data/.github/workflows/github-release.yml +58 -0
- data/.github/workflows/release.yml +221 -0
- data/CHANGELOG.md +33 -0
- data/Gemfile +4 -1
- data/HOOKS.md +23 -14
- data/README.md +32 -9
- data/backburner.gemspec +6 -1
- data/bin/backburner +1 -1
- data/lib/active_job/queue_adapters/backburner_adapter.rb +41 -0
- data/lib/backburner/configuration.rb +7 -0
- data/lib/backburner/connection.rb +4 -2
- data/lib/backburner/enqueue_pool.rb +88 -0
- data/lib/backburner/helpers.rb +77 -22
- data/lib/backburner/job.rb +46 -7
- data/lib/backburner/logger.rb +9 -0
- data/lib/backburner/performable.rb +1 -1
- data/lib/backburner/queue.rb +37 -19
- data/lib/backburner/version.rb +1 -1
- data/lib/backburner/worker.rb +112 -21
- data/lib/backburner/workers/forking.rb +4 -2
- data/lib/backburner/workers/simple.rb +5 -0
- data/lib/backburner/workers/threading.rb +1 -7
- data/lib/backburner/workers/threads_on_fork.rb +58 -23
- data/lib/backburner.rb +7 -0
- data/test/active_job_adapter_test.rb +24 -0
- data/test/back_burner_test.rb +13 -0
- data/test/connection_test.rb +22 -0
- data/test/fixtures/active_job_app.rb +53 -0
- data/test/fixtures/active_jobs.rb +11 -0
- data/test/fixtures/autoload/test_broken_name_job.rb +4 -0
- data/test/fixtures/autoload/test_broken_no_method_job.rb +4 -0
- data/test/fixtures/autoload/test_broken_require_job.rb +4 -0
- data/test/fixtures/autoload/test_broken_runtime_job.rb +4 -0
- data/test/fixtures/autoload/test_broken_syntax_job.rb +4 -0
- data/test/fixtures/autoload/test_broken_timeout_job.rb +4 -0
- data/test/fixtures/autoload/test_same_name_job.rb +5 -0
- data/test/fixtures/test_fork_jobs.rb +5 -0
- data/test/fixtures/test_jobs.rb +53 -0
- data/test/fixtures/test_queue_settings.rb +41 -1
- data/test/helpers/templogger.rb +1 -1
- data/test/helpers_test.rb +184 -0
- data/test/job_test.rb +22 -0
- data/test/logger_test.rb +27 -0
- data/test/performable_test.rb +50 -0
- data/test/queue_test.rb +63 -0
- data/test/test_helper.rb +59 -5
- data/test/worker_test.rb +346 -1
- data/test/workers/forking_worker_test.rb +19 -1
- data/test/workers/simple_worker_test.rb +272 -0
- data/test/workers/threading_worker_test.rb +22 -1
- data/test/workers/threads_on_fork_worker_test.rb +279 -12
- metadata +87 -8
- data/.travis.yml +0 -29
- data/circle.yml +0 -3
data/test/worker_test.rb
CHANGED
|
@@ -5,7 +5,7 @@ require File.expand_path('../fixtures/hooked', __FILE__)
|
|
|
5
5
|
describe "Backburner::Worker module" do
|
|
6
6
|
before do
|
|
7
7
|
Backburner.default_queues.clear
|
|
8
|
-
clear_jobs!(Backburner.configuration.primary_queue, "test-plain", "test.bar", "bar.baz.foo")
|
|
8
|
+
clear_jobs!(Backburner.configuration.primary_queue, "test-plain", "test.bar", "bar.baz.foo", "test-parent")
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
describe "for enqueue class method" do
|
|
@@ -49,6 +49,28 @@ describe "Backburner::Worker module" do
|
|
|
49
49
|
end
|
|
50
50
|
end # queue respond_timeout
|
|
51
51
|
|
|
52
|
+
it "should enqueue a subclass job on its superclass queue with its settings" do
|
|
53
|
+
Backburner::Worker.enqueue TestChildQueueJob, [3, 4]
|
|
54
|
+
pop_one_job("test-parent") do |job, body|
|
|
55
|
+
assert_equal "TestChildQueueJob", body["class"]
|
|
56
|
+
assert_equal [3, 4], body["args"]
|
|
57
|
+
assert_equal 250, job.ttr
|
|
58
|
+
assert_equal 0, job.pri
|
|
59
|
+
end
|
|
60
|
+
end # subclass queue
|
|
61
|
+
|
|
62
|
+
it "should support a priority label as the default priority" do
|
|
63
|
+
default_priority = Backburner.configuration.default_priority
|
|
64
|
+
Backburner.configuration.default_priority = :low
|
|
65
|
+
Backburner::Worker.enqueue TestPlainJob, [1, 2]
|
|
66
|
+
pop_one_job("test-plain") do |job, body|
|
|
67
|
+
assert_equal "TestPlainJob", body["class"]
|
|
68
|
+
assert_equal 200, job.pri
|
|
69
|
+
end
|
|
70
|
+
ensure
|
|
71
|
+
Backburner.configuration.default_priority = default_priority
|
|
72
|
+
end # default priority label
|
|
73
|
+
|
|
52
74
|
it "should support enqueuing job with custom queue" do
|
|
53
75
|
Backburner::Worker.enqueue TestJob, [6, 7], :queue => "test.bar", :pri => 5000
|
|
54
76
|
pop_one_job("test.bar") do |job, body|
|
|
@@ -60,6 +82,31 @@ describe "Backburner::Worker module" do
|
|
|
60
82
|
end
|
|
61
83
|
end # custom
|
|
62
84
|
|
|
85
|
+
it "should round a fractional delay up to whole seconds, so the job never runs early" do
|
|
86
|
+
conn = beanstalk_connection
|
|
87
|
+
{ nil => 0, -1 => 0, 0 => 0, 0.2 => 1, 1 => 1, 1.5 => 2, 2 => 2 }.each do |delay, expected|
|
|
88
|
+
response = Backburner::Worker.enqueue TestPlainJob, [1, 2], :delay => delay
|
|
89
|
+
assert_equal expected, conn.jobs.find(response[:id]).stats.delay, "delay #{delay.inspect}"
|
|
90
|
+
end
|
|
91
|
+
ensure
|
|
92
|
+
conn.close if conn
|
|
93
|
+
clear_jobs!("test-plain")
|
|
94
|
+
end # fractional delay
|
|
95
|
+
|
|
96
|
+
it "should round a fractional TTR up to whole seconds, from an option, a class or the default" do
|
|
97
|
+
respond_timeout = Backburner.configuration.respond_timeout
|
|
98
|
+
conn = beanstalk_connection
|
|
99
|
+
ttr_of = lambda { |response| conn.jobs.find(response[:id]).stats.ttr }
|
|
100
|
+
assert_equal 1, ttr_of.call(Backburner::Worker.enqueue(TestPlainJob, [1, 2], :ttr => 0.2))
|
|
101
|
+
assert_equal 2, ttr_of.call(Backburner::Worker.enqueue(TestFloatRespondTimeoutJob, []))
|
|
102
|
+
Backburner.configuration.respond_timeout = 2.5
|
|
103
|
+
assert_equal 3, ttr_of.call(Backburner::Worker.enqueue(TestPlainJob, [1, 2]))
|
|
104
|
+
ensure
|
|
105
|
+
Backburner.configuration.respond_timeout = respond_timeout
|
|
106
|
+
conn.close if conn
|
|
107
|
+
clear_jobs!("test-plain")
|
|
108
|
+
end # fractional ttr
|
|
109
|
+
|
|
63
110
|
it "should support async job" do
|
|
64
111
|
TestAsyncJob.async(:ttr => 100, :queue => "bar.baz.foo").foo(10, 5)
|
|
65
112
|
pop_one_job("bar.baz.foo") do |job, body|
|
|
@@ -80,6 +127,282 @@ describe "Backburner::Worker module" do
|
|
|
80
127
|
end
|
|
81
128
|
end # enqueue
|
|
82
129
|
|
|
130
|
+
describe "for the enqueue connections" do
|
|
131
|
+
before do
|
|
132
|
+
GC.start # closes the connections earlier tests left to the garbage collector before a test counts them
|
|
133
|
+
@gc_was_disabled = GC.disable # and keeps a collection from closing one while it counts
|
|
134
|
+
@stats_connection = beanstalk_connection
|
|
135
|
+
@beanstalk_url = Backburner.configuration.beanstalk_url
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
after do
|
|
139
|
+
GC.enable unless @gc_was_disabled
|
|
140
|
+
@stats_connection.close
|
|
141
|
+
Backburner.configuration.beanstalk_url = @beanstalk_url
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Connections beanstalkd has accepted so far
|
|
145
|
+
def total_connections
|
|
146
|
+
@stats_connection.stats.total_connections
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def open_connections
|
|
150
|
+
@stats_connection.stats.current_connections
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def enqueue(n = 0)
|
|
154
|
+
Backburner::Worker.enqueue(TestJob, [n, 0])
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Starts the pool afresh: a new URL (spelling the same beanstalkd) closes its idle connections.
|
|
158
|
+
# Leaves one idle connection in it.
|
|
159
|
+
def fresh_pool
|
|
160
|
+
["beanstalk://127.0.0.1:11300", "beanstalk://127.0.0.1:11300/"].each do |url|
|
|
161
|
+
Backburner.configuration.beanstalk_url = url
|
|
162
|
+
enqueue
|
|
163
|
+
end
|
|
164
|
+
settle
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Waits for beanstalkd to have seen the connections closed so far
|
|
168
|
+
def settle
|
|
169
|
+
last = nil
|
|
170
|
+
wait_for(5) { (now = open_connections) == last || (last = now; sleep 0.1; false) }
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
it "should reuse a connection for sequential enqueues" do
|
|
174
|
+
enqueue
|
|
175
|
+
before_count = total_connections
|
|
176
|
+
responses = Array.new(5) { |i| enqueue(i) }
|
|
177
|
+
assert_equal ["INSERTED"] * 5, responses.map { |res| res[:status] }
|
|
178
|
+
assert_equal 0, total_connections - before_count
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
it "should reuse a connection across fibers and threads" do
|
|
182
|
+
enqueue
|
|
183
|
+
before_count = total_connections
|
|
184
|
+
5.times { |i| Fiber.new { enqueue(i) }.resume }
|
|
185
|
+
5.times { |i| Thread.new { enqueue(i) }.join }
|
|
186
|
+
assert_equal 0, total_connections - before_count
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
it "should keep at most 5 idle connections after concurrent enqueues" do
|
|
190
|
+
fresh_pool # one idle connection
|
|
191
|
+
before_total, before_open = total_connections, open_connections
|
|
192
|
+
threads, inside = 8, Queue.new
|
|
193
|
+
release = Queue.new
|
|
194
|
+
serializer = Backburner.configuration.job_serializer_proc
|
|
195
|
+
# every thread holds a connection until all of them do
|
|
196
|
+
Backburner.configuration.job_serializer_proc = lambda { |body| inside << 1; release.pop; body.to_json }
|
|
197
|
+
workers = Array.new(threads) { |i| Thread.new { enqueue(i) } }
|
|
198
|
+
threads.times { inside.pop }
|
|
199
|
+
threads.times { release << 1 }
|
|
200
|
+
workers.each(&:join)
|
|
201
|
+
Backburner.configuration.job_serializer_proc = serializer
|
|
202
|
+
assert_equal threads - 1, total_connections - before_total, "one reused, the rest opened"
|
|
203
|
+
kept = 5
|
|
204
|
+
wait_for(5) { open_connections - before_open == kept - 1 }
|
|
205
|
+
assert_equal kept - 1, open_connections - before_open, "only #{kept} stay open"
|
|
206
|
+
ensure
|
|
207
|
+
Backburner.configuration.job_serializer_proc = serializer if serializer
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
it "should give a forked child its own connection" do
|
|
211
|
+
skip "needs fork" unless Process.respond_to?(:fork)
|
|
212
|
+
enqueue(1)
|
|
213
|
+
before_child = total_connections
|
|
214
|
+
pid = Process.fork do
|
|
215
|
+
res = Backburner::Worker.enqueue(TestJob, [2, 0]) rescue nil
|
|
216
|
+
exit!(res && res[:status] == "INSERTED" ? 0 : 1)
|
|
217
|
+
end
|
|
218
|
+
Process.wait(pid)
|
|
219
|
+
assert_equal 0, $?.exitstatus, "the child's enqueue failed"
|
|
220
|
+
assert_equal 1, total_connections - before_child, "the child should open its own connection"
|
|
221
|
+
assert_equal "INSERTED", enqueue(3)[:status]
|
|
222
|
+
assert_equal 1, total_connections - before_child, "the parent should keep its connection"
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
it "should close a connection that an error interrupted instead of reusing it" do
|
|
226
|
+
fresh_pool # one idle connection
|
|
227
|
+
before_total, before_open = total_connections, open_connections
|
|
228
|
+
serializer = Backburner.configuration.job_serializer_proc
|
|
229
|
+
Backburner.configuration.job_serializer_proc = lambda { |body| raise Timeout::Error }
|
|
230
|
+
assert_raises(Timeout::Error) { enqueue(2) }
|
|
231
|
+
Backburner.configuration.job_serializer_proc = serializer
|
|
232
|
+
wait_for(5) { open_connections == before_open - 1 }
|
|
233
|
+
assert_equal before_open - 1, open_connections, "the interrupted connection should be closed"
|
|
234
|
+
assert_equal "INSERTED", enqueue(3)[:status]
|
|
235
|
+
assert_equal 1, total_connections - before_total, "the next enqueue should open a new connection"
|
|
236
|
+
ensure
|
|
237
|
+
Backburner.configuration.job_serializer_proc = serializer if serializer
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
it "should close a connection whose enqueue was killed" do
|
|
241
|
+
fresh_pool # one idle connection
|
|
242
|
+
before_total, before_open = total_connections, open_connections
|
|
243
|
+
serializer = Backburner.configuration.job_serializer_proc
|
|
244
|
+
inside = Queue.new
|
|
245
|
+
Backburner.configuration.job_serializer_proc = lambda { |body| inside << 1; sleep }
|
|
246
|
+
thread = Thread.new { enqueue(2) }
|
|
247
|
+
inside.pop
|
|
248
|
+
thread.kill.join
|
|
249
|
+
Backburner.configuration.job_serializer_proc = serializer
|
|
250
|
+
wait_for(5) { open_connections == before_open - 1 }
|
|
251
|
+
assert_equal before_open - 1, open_connections, "the killed enqueue's connection should be closed"
|
|
252
|
+
assert_equal "INSERTED", enqueue(3)[:status]
|
|
253
|
+
assert_equal 1, total_connections - before_total, "the next enqueue should open a new connection"
|
|
254
|
+
ensure
|
|
255
|
+
Backburner.configuration.job_serializer_proc = serializer if serializer
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
it "should replace a connection idle for longer than enqueue_connection_max_idle" do
|
|
259
|
+
max_idle = Backburner.configuration.enqueue_connection_max_idle
|
|
260
|
+
fresh_pool # one idle connection
|
|
261
|
+
before_total, before_open = total_connections, open_connections
|
|
262
|
+
Backburner.configuration.enqueue_connection_max_idle = 0.05
|
|
263
|
+
sleep 0.1
|
|
264
|
+
assert_equal "INSERTED", enqueue(1)[:status]
|
|
265
|
+
assert_equal 1, total_connections - before_total, "the expired connection should be replaced"
|
|
266
|
+
wait_for(5) { open_connections == before_open }
|
|
267
|
+
assert_equal before_open, open_connections, "the expired connection should be closed"
|
|
268
|
+
Backburner.configuration.enqueue_connection_max_idle = 60
|
|
269
|
+
assert_equal "INSERTED", enqueue(2)[:status]
|
|
270
|
+
assert_equal 1, total_connections - before_total, "a fresh connection should be reused"
|
|
271
|
+
ensure
|
|
272
|
+
Backburner.configuration.enqueue_connection_max_idle = max_idle
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
describe "when beanstalkd goes away" do
|
|
276
|
+
before do
|
|
277
|
+
pick_beanstalkd_port
|
|
278
|
+
@beanstalk_url = Backburner.configuration.beanstalk_url
|
|
279
|
+
Backburner.configuration.beanstalk_url = "beanstalk://127.0.0.1:#{@port}"
|
|
280
|
+
start_beanstalkd
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
after do
|
|
284
|
+
stop_beanstalkd
|
|
285
|
+
Backburner.configuration.beanstalk_url = @beanstalk_url if @beanstalk_url
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
it "should recover from a restart and from Beaneater::NotConnected" do
|
|
289
|
+
Thread.new do
|
|
290
|
+
assert_equal "INSERTED", Backburner::Worker.enqueue(TestJob, [1, 0])[:status]
|
|
291
|
+
stop_beanstalkd
|
|
292
|
+
start_beanstalkd
|
|
293
|
+
assert_equal "INSERTED", Backburner::Worker.enqueue(TestJob, [2, 0])[:status]
|
|
294
|
+
stop_beanstalkd
|
|
295
|
+
assert_raises(Beaneater::NotConnected) { Backburner::Worker.enqueue(TestJob, [3, 0]) }
|
|
296
|
+
start_beanstalkd
|
|
297
|
+
assert_equal "INSERTED", Backburner::Worker.enqueue(TestJob, [4, 0])[:status]
|
|
298
|
+
end.join
|
|
299
|
+
end
|
|
300
|
+
end # beanstalkd goes away
|
|
301
|
+
end # enqueue connection
|
|
302
|
+
|
|
303
|
+
describe "without a tube_namespace" do
|
|
304
|
+
before { @tube_namespace = Backburner.configuration.tube_namespace }
|
|
305
|
+
after { Backburner.configuration.tube_namespace = @tube_namespace }
|
|
306
|
+
|
|
307
|
+
def reserve_from(tube_name)
|
|
308
|
+
connection = beanstalk_connection
|
|
309
|
+
connection.tubes.watch!(tube_name)
|
|
310
|
+
job = connection.tubes.reserve(1)
|
|
311
|
+
job.delete
|
|
312
|
+
Backburner.configuration.job_parser_proc.call(job.body)
|
|
313
|
+
ensure
|
|
314
|
+
connection.close if connection
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
it "should enqueue on unprefixed tubes when tube_namespace is nil or false" do
|
|
318
|
+
[nil, false].each do |namespace|
|
|
319
|
+
Backburner.configuration.tube_namespace = namespace
|
|
320
|
+
Backburner::Worker.enqueue TestJob, [1, namespace.inspect]
|
|
321
|
+
Backburner::Worker.enqueue TestJob, [2, namespace.inspect], :queue => "test-no-namespace"
|
|
322
|
+
assert_equal [1, namespace.inspect], reserve_from("backburner-jobs")["args"]
|
|
323
|
+
assert_equal [2, namespace.inspect], reserve_from("test-no-namespace")["args"]
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
it "should enqueue on the tubes 1.7.0 used when tube_namespace is empty" do
|
|
328
|
+
Backburner.configuration.tube_namespace = ""
|
|
329
|
+
Backburner::Worker.enqueue TestJob, [1, 2]
|
|
330
|
+
Backburner::Worker.enqueue TestJob, [3, 4], :queue => "test-no-namespace"
|
|
331
|
+
assert_equal [1, 2], reserve_from(".backburner-jobs")["args"]
|
|
332
|
+
assert_equal [3, 4], reserve_from(".test-no-namespace")["args"]
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
it "should list existing unprefixed tubes for a worker given no tubes" do
|
|
336
|
+
Backburner.configuration.tube_namespace = false
|
|
337
|
+
Backburner::Worker.enqueue TestJob, [1, 2], :queue => "test-no-namespace"
|
|
338
|
+
assert_contains Backburner::Worker.new(nil).tube_names, "test-no-namespace"
|
|
339
|
+
reserve_from("test-no-namespace")
|
|
340
|
+
end
|
|
341
|
+
end # without a tube_namespace
|
|
342
|
+
|
|
343
|
+
describe "for a worker given no tubes" do
|
|
344
|
+
before do
|
|
345
|
+
@tube_namespace = Backburner.configuration.tube_namespace
|
|
346
|
+
@namespace_separator = Backburner.configuration.namespace_separator
|
|
347
|
+
end
|
|
348
|
+
after do
|
|
349
|
+
Backburner.configuration.tube_namespace = @tube_namespace
|
|
350
|
+
Backburner.configuration.namespace_separator = @namespace_separator
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
# Namespaces, separators and queue names (ActiveJob's queue_name_prefix gives "production_mailers") whose
|
|
354
|
+
# tubes the worker used to miss: it expanded the tube names it found on the server again ("a--b" raised)
|
|
355
|
+
it "should watch the tubes enqueued on for its namespace, under their names on the server" do
|
|
356
|
+
[["MyApp", ".", "default"], ["jobs(eu)", ".", "default"], ["app+", ".", "default"], ["bb-discovery", "_", "default"],
|
|
357
|
+
["production", ".", "production_mailers"], ["bb-discovery", ".", "report_2026"], ["bb-discovery", ".", "a__b"]].each do |namespace, separator, queue|
|
|
358
|
+
Backburner.configuration.tube_namespace = namespace
|
|
359
|
+
Backburner.configuration.namespace_separator = separator
|
|
360
|
+
tube_name = Backburner::Worker.expand_tube_name(queue)
|
|
361
|
+
connection = beanstalk_connection # no jobs left in this namespace for the worker to take first
|
|
362
|
+
connection.tubes.all.select { |tube| tube.name.start_with?("#{namespace}#{separator}") }.each(&:clear)
|
|
363
|
+
connection.close
|
|
364
|
+
Backburner::Worker.enqueue TestJob, [1, 2], :queue => queue
|
|
365
|
+
|
|
366
|
+
[Backburner::Workers::Simple, Backburner::Workers::Forking, Backburner::Workers::Threading, Backburner::Workers::ThreadsOnFork].each do |worker_class|
|
|
367
|
+
worker = worker_class.new(nil)
|
|
368
|
+
capture_stdout { worker.prepare }
|
|
369
|
+
worker.connection.close
|
|
370
|
+
assert_contains worker.tube_names, tube_name, "#{worker_class.name} #{[namespace, separator, queue].inspect}"
|
|
371
|
+
end
|
|
372
|
+
$worker_test_count = 0
|
|
373
|
+
worker = Backburner::Workers::Simple.new(nil)
|
|
374
|
+
silenced(3) { worker.prepare; worker.work_one_job }
|
|
375
|
+
worker.connection.close
|
|
376
|
+
assert_equal 3, $worker_test_count, [namespace, separator, queue].inspect
|
|
377
|
+
end
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
# Another app's tubes on a shared server: the worker expanded them into tubes nobody enqueues on, so it never took
|
|
381
|
+
# their jobs, and mustn't start to
|
|
382
|
+
it "should leave out the tubes its namespace and separator can't give" do
|
|
383
|
+
[[["bb-discovery_admin", "_"], ["bb-discovery", "_"]], [["MyApp.Admin", "."], ["MyApp", "."]],
|
|
384
|
+
[["bb-other", "."], ["bb:disc", "."]]].each do |(other_namespace, other_separator), (namespace, separator)|
|
|
385
|
+
Backburner.configuration.tube_namespace = other_namespace
|
|
386
|
+
Backburner.configuration.namespace_separator = other_separator
|
|
387
|
+
other_tube = Backburner::Worker.expand_tube_name("reports")
|
|
388
|
+
begin
|
|
389
|
+
Backburner::Worker.enqueue TestJob, [1, 2], :queue => "reports"
|
|
390
|
+
Backburner.configuration.tube_namespace = namespace
|
|
391
|
+
Backburner.configuration.namespace_separator = separator
|
|
392
|
+
worker = Backburner::Workers::Simple.new(nil)
|
|
393
|
+
capture_stdout { worker.prepare }
|
|
394
|
+
worker.connection.close
|
|
395
|
+
refute_includes worker.tube_names, other_tube, namespace
|
|
396
|
+
assert_equal ["bb"], worker.tube_names, namespace if namespace == "bb:disc" # a ':' cuts every tube name to "bb"
|
|
397
|
+
ensure
|
|
398
|
+
connection = beanstalk_connection
|
|
399
|
+
connection.tubes.find(other_tube).clear
|
|
400
|
+
connection.close
|
|
401
|
+
end
|
|
402
|
+
end
|
|
403
|
+
end
|
|
404
|
+
end # given no tubes
|
|
405
|
+
|
|
83
406
|
describe "for start class method" do
|
|
84
407
|
it "should initialize and start the worker instance" do
|
|
85
408
|
ech = stub
|
|
@@ -89,6 +412,28 @@ describe "Backburner::Worker module" do
|
|
|
89
412
|
end
|
|
90
413
|
end # start
|
|
91
414
|
|
|
415
|
+
describe "for gc_disabled_on_exit? class method" do
|
|
416
|
+
it "should be true only on CRuby 3.3, the Ruby whose forked children can crash as they exit" do
|
|
417
|
+
assert Backburner::Worker.gc_disabled_on_exit?("ruby", "3.3.6")
|
|
418
|
+
%w(2.7.8 3.2.6 3.4.1 4.0.0).each do |version|
|
|
419
|
+
refute Backburner::Worker.gc_disabled_on_exit?("ruby", version), version
|
|
420
|
+
end
|
|
421
|
+
refute Backburner::Worker.gc_disabled_on_exit?("jruby", "3.3.0")
|
|
422
|
+
end
|
|
423
|
+
end # gc_disabled_on_exit?
|
|
424
|
+
|
|
425
|
+
describe "with an anonymous subclass of a queue class" do
|
|
426
|
+
before { @anonymous = Class.new(TestNamedQueueJob) }
|
|
427
|
+
after { Backburner::Worker.known_queue_classes.delete(@anonymous) }
|
|
428
|
+
|
|
429
|
+
it "should leave it out of the known queues of a worker given no tubes" do
|
|
430
|
+
[Backburner::Workers::Simple, Backburner::Workers::Threading, Backburner::Workers::ThreadsOnFork].each do |worker_class|
|
|
431
|
+
assert_contains worker_class.new(nil).tube_names.map { |name| Backburner::Worker.expand_tube_name(name) },
|
|
432
|
+
Backburner::Worker.expand_tube_name("testnamedqueuejob"), worker_class.name
|
|
433
|
+
end
|
|
434
|
+
end
|
|
435
|
+
end # anonymous subclass
|
|
436
|
+
|
|
92
437
|
describe "for tube_names accessor" do
|
|
93
438
|
before do
|
|
94
439
|
Backburner.default_queues << "baz"
|
|
@@ -70,11 +70,29 @@ describe "Backburner::Workers::Forking module" do
|
|
|
70
70
|
@worker_class.any_instance.expects(:coolest_exit)
|
|
71
71
|
Process.expects(:wait).with(fake_pid)
|
|
72
72
|
|
|
73
|
+
gc_was_disabled = GC.disable # the child's body runs in this process, and disables the GC as it ends
|
|
74
|
+
GC.enable unless gc_was_disabled
|
|
73
75
|
silenced(2) do
|
|
74
76
|
worker = @worker_class.new('bar.foo')
|
|
75
77
|
worker.prepare
|
|
76
78
|
worker.fork_one_job
|
|
77
79
|
end
|
|
80
|
+
ensure
|
|
81
|
+
GC.enable unless gc_was_disabled
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# A child leaves work_one_job by an exception when a job calls exit, on a TERM or INT, or on an
|
|
85
|
+
# error work_one_job doesn't rescue, and then exits through the interpreter's teardown
|
|
86
|
+
[:raise, :shutdown].each do |how|
|
|
87
|
+
it "should disable the GC in the child before the interpreter's teardown (#{how})" do
|
|
88
|
+
Backburner::Worker.stubs(:gc_disabled_on_exit?).returns(true) # as on Ruby 3.3
|
|
89
|
+
worker = @worker_class.new('bar.foo')
|
|
90
|
+
gc_disabled = gc_disabled_at_child_exit?(worker, how) do |body|
|
|
91
|
+
worker.define_singleton_method(:work_one_job) { |*| body.call }
|
|
92
|
+
worker.fork_one_job
|
|
93
|
+
end
|
|
94
|
+
assert gc_disabled, "the child's GC was still enabled when it exited"
|
|
95
|
+
end
|
|
78
96
|
end
|
|
79
97
|
|
|
80
98
|
end # fork_one_job
|
|
@@ -96,7 +114,7 @@ describe "Backburner::Workers::Forking module" do
|
|
|
96
114
|
|
|
97
115
|
after do
|
|
98
116
|
@templogger.close
|
|
99
|
-
Backburner.
|
|
117
|
+
Backburner.configure { |config| config.max_job_retries = 0; config.retry_delay = 5; config.logger = nil }
|
|
100
118
|
clear_jobs!('response')
|
|
101
119
|
clear_jobs!('bar.foo.1', 'bar.foo.2', 'bar.foo.3', 'bar.foo.4', 'bar.foo.5')
|
|
102
120
|
end
|