inst-jobs 2.4.10 → 3.1.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/db/migrate/20210929204903_update_conflicting_singleton_function_to_use_index.rb +27 -0
  3. data/db/migrate/20211101190934_update_after_delete_trigger_for_singleton_index.rb +137 -0
  4. data/db/migrate/20211207094200_update_after_delete_trigger_for_singleton_transition_cases.rb +171 -0
  5. data/db/migrate/20211220112800_fix_singleton_race_condition_insert.rb +59 -0
  6. data/db/migrate/20211220113000_fix_singleton_race_condition_delete.rb +207 -0
  7. data/db/migrate/20220127091200_fix_singleton_unique_constraint.rb +31 -0
  8. data/db/migrate/20220128084800_update_insert_trigger_for_singleton_unique_constraint_change.rb +60 -0
  9. data/db/migrate/20220128084900_update_delete_trigger_for_singleton_unique_constraint_change.rb +209 -0
  10. data/db/migrate/20220203063200_remove_old_singleton_index.rb +31 -0
  11. data/db/migrate/20220328152900_add_failed_jobs_indicies.rb +12 -0
  12. data/db/migrate/20220519204546_add_requeued_job_id_to_failed_jobs.rb +7 -0
  13. data/exe/inst_jobs +1 -1
  14. data/lib/delayed/backend/active_record.rb +62 -15
  15. data/lib/delayed/backend/base.rb +20 -5
  16. data/lib/delayed/batch.rb +1 -1
  17. data/lib/delayed/lifecycle.rb +8 -1
  18. data/lib/delayed/message_sending.rb +1 -1
  19. data/lib/delayed/periodic.rb +1 -1
  20. data/lib/delayed/pool.rb +12 -2
  21. data/lib/delayed/rails_reloader_plugin.rb +30 -0
  22. data/lib/delayed/server.rb +8 -2
  23. data/lib/delayed/settings.rb +3 -1
  24. data/lib/delayed/version.rb +1 -1
  25. data/lib/delayed/work_queue/parent_process/server.rb +43 -12
  26. data/lib/delayed/worker/health_check.rb +1 -1
  27. data/lib/delayed/worker/process_helper.rb +3 -3
  28. data/lib/delayed/worker.rb +6 -24
  29. metadata +65 -83
  30. data/spec/active_record_job_spec.rb +0 -294
  31. data/spec/delayed/cli_spec.rb +0 -25
  32. data/spec/delayed/daemon_spec.rb +0 -38
  33. data/spec/delayed/message_sending_spec.rb +0 -108
  34. data/spec/delayed/periodic_spec.rb +0 -32
  35. data/spec/delayed/server_spec.rb +0 -103
  36. data/spec/delayed/settings_spec.rb +0 -48
  37. data/spec/delayed/work_queue/in_process_spec.rb +0 -31
  38. data/spec/delayed/work_queue/parent_process/client_spec.rb +0 -87
  39. data/spec/delayed/work_queue/parent_process/server_spec.rb +0 -233
  40. data/spec/delayed/work_queue/parent_process_spec.rb +0 -60
  41. data/spec/delayed/worker/consul_health_check_spec.rb +0 -63
  42. data/spec/delayed/worker/health_check_spec.rb +0 -134
  43. data/spec/delayed/worker_spec.rb +0 -100
  44. data/spec/migrate/20140924140513_add_story_table.rb +0 -9
  45. data/spec/sample_jobs.rb +0 -79
  46. data/spec/shared/delayed_batch.rb +0 -105
  47. data/spec/shared/delayed_method.rb +0 -287
  48. data/spec/shared/performable_method.rb +0 -75
  49. data/spec/shared/shared_backend.rb +0 -989
  50. data/spec/shared/testing.rb +0 -50
  51. data/spec/shared/worker.rb +0 -413
  52. data/spec/shared_jobs_specs.rb +0 -17
  53. data/spec/spec_helper.rb +0 -134
@@ -1,989 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module InDelayedJobTest
4
- def self.check_in_job
5
- Delayed::Job.in_delayed_job?.should == true
6
- end
7
- end
8
-
9
- shared_examples_for "a backend" do
10
- def create_job(opts = {})
11
- Delayed::Job.enqueue(SimpleJob.new, **{ queue: nil }.merge(opts))
12
- end
13
-
14
- before do
15
- SimpleJob.runs = 0
16
- end
17
-
18
- it "sets run_at automatically if not set" do
19
- expect(Delayed::Job.create(payload_object: ErrorJob.new).run_at).not_to be_nil
20
- end
21
-
22
- it "does not set run_at automatically if already set" do
23
- later = Delayed::Job.db_time_now + 5.minutes
24
- expect(Delayed::Job.create(payload_object: ErrorJob.new, run_at: later).run_at).to be_within(1).of(later)
25
- end
26
-
27
- it "raises ArgumentError when handler doesn't respond_to :perform" do
28
- expect { Delayed::Job.enqueue(Object.new) }.to raise_error(ArgumentError)
29
- end
30
-
31
- it "increases count after enqueuing items" do
32
- Delayed::Job.enqueue SimpleJob.new
33
- expect(Delayed::Job.jobs_count(:current)).to eq(1)
34
- end
35
-
36
- it "is able to set priority when enqueuing items" do
37
- @job = Delayed::Job.enqueue SimpleJob.new, priority: 5
38
- expect(@job.priority).to eq(5)
39
- end
40
-
41
- it "uses the default priority when enqueuing items" do
42
- Delayed::Job.default_priority = 0
43
- @job = Delayed::Job.enqueue SimpleJob.new
44
- expect(@job.priority).to eq(0)
45
- Delayed::Job.default_priority = 10
46
- @job = Delayed::Job.enqueue SimpleJob.new
47
- expect(@job.priority).to eq(10)
48
- Delayed::Job.default_priority = 0
49
- end
50
-
51
- it "is able to set run_at when enqueuing items" do
52
- later = Delayed::Job.db_time_now + 5.minutes
53
- @job = Delayed::Job.enqueue SimpleJob.new, priority: 5, run_at: later
54
- expect(@job.run_at).to be_within(1).of(later)
55
- end
56
-
57
- it "is able to set expires_at when enqueuing items" do
58
- later = Delayed::Job.db_time_now + 1.day
59
- @job = Delayed::Job.enqueue SimpleJob.new, expires_at: later
60
- expect(@job.expires_at).to be_within(1).of(later)
61
- end
62
-
63
- it "works with jobs in modules" do
64
- M::ModuleJob.runs = 0
65
- job = Delayed::Job.enqueue M::ModuleJob.new
66
- expect { job.invoke_job }.to change { M::ModuleJob.runs }.from(0).to(1)
67
- end
68
-
69
- it "raises an DeserializationError when the job class is totally unknown" do
70
- job = Delayed::Job.new handler: "--- !ruby/object:JobThatDoesNotExist {}"
71
- expect { job.payload_object.perform }.to raise_error(Delayed::Backend::DeserializationError)
72
- end
73
-
74
- it "tries to load the class when it is unknown at the time of the deserialization" do
75
- job = Delayed::Job.new handler: "--- !ruby/object:JobThatDoesNotExist {}"
76
- expect { job.payload_object.perform }.to raise_error(Delayed::Backend::DeserializationError)
77
- end
78
-
79
- it "tries include the namespace when loading unknown objects" do
80
- job = Delayed::Job.new handler: "--- !ruby/object:Delayed::JobThatDoesNotExist {}"
81
- expect { job.payload_object.perform }.to raise_error(Delayed::Backend::DeserializationError)
82
- end
83
-
84
- it "alsoes try to load structs when they are unknown (raises TypeError)" do
85
- job = Delayed::Job.new handler: "--- !ruby/struct:JobThatDoesNotExist {}"
86
- expect { job.payload_object.perform }.to raise_error(Delayed::Backend::DeserializationError)
87
- end
88
-
89
- it "tries include the namespace when loading unknown structs" do
90
- job = Delayed::Job.new handler: "--- !ruby/struct:Delayed::JobThatDoesNotExist {}"
91
- expect { job.payload_object.perform }.to raise_error(Delayed::Backend::DeserializationError)
92
- end
93
-
94
- it "raises an DeserializationError when the handler is invalid YAML" do
95
- job = Delayed::Job.new handler: %(test: ""11")
96
- expect { job.payload_object.perform }.to raise_error(Delayed::Backend::DeserializationError, /parsing error/)
97
- end
98
-
99
- describe "find_available" do
100
- it "does not find failed jobs" do
101
- @job = create_job attempts: 50
102
- @job.fail!
103
- expect(Delayed::Job.find_available(5)).not_to include(@job)
104
- end
105
-
106
- it "does not find jobs scheduled for the future" do
107
- @job = create_job run_at: (Delayed::Job.db_time_now + 1.minute)
108
- expect(Delayed::Job.find_available(5)).not_to include(@job)
109
- end
110
-
111
- it "does not find jobs locked by another worker" do
112
- @job = create_job
113
- expect(Delayed::Job.get_and_lock_next_available("other_worker")).to eq(@job)
114
- expect(Delayed::Job.find_available(5)).not_to include(@job)
115
- end
116
-
117
- it "finds open jobs" do
118
- @job = create_job
119
- expect(Delayed::Job.find_available(5)).to include(@job)
120
- end
121
-
122
- it "returns an empty hash when asking for multiple jobs, and there aren't any" do
123
- locked_jobs = Delayed::Job.get_and_lock_next_available(%w[worker1 worker2])
124
- expect(locked_jobs).to eq({})
125
- end
126
- end
127
-
128
- context "when another worker is already performing an task, it" do
129
- before do
130
- @job = Delayed::Job.create payload_object: SimpleJob.new
131
- expect(Delayed::Job.get_and_lock_next_available("worker1")).to eq(@job)
132
- end
133
-
134
- it "does not allow a second worker to get exclusive access" do
135
- expect(Delayed::Job.get_and_lock_next_available("worker2")).to be_nil
136
- end
137
-
138
- it "is not found by another worker" do
139
- expect(Delayed::Job.find_available(1).length).to eq(0)
140
- end
141
- end
142
-
143
- describe "#name" do
144
- it "is the class name of the job that was enqueued" do
145
- expect(Delayed::Job.create(payload_object: ErrorJob.new).name).to eq("ErrorJob")
146
- end
147
-
148
- it "is the method that will be called if its a performable method object" do
149
- @job = Story.delay(ignore_transaction: true).create
150
- expect(@job.name).to eq("Story.create")
151
- end
152
-
153
- it "is the instance method that will be called if its a performable method object" do
154
- @job = Story.create(text: "...").delay(ignore_transaction: true).save
155
- expect(@job.name).to eq("Story#save")
156
- end
157
- end
158
-
159
- context "worker prioritization" do
160
- it "fetches jobs ordered by priority" do
161
- 10.times { create_job priority: rand(10) }
162
- jobs = Delayed::Job.find_available(10)
163
- expect(jobs.size).to eq(10)
164
- jobs.each_cons(2) do |a, b|
165
- expect(a.priority).to be <= b.priority
166
- end
167
- end
168
-
169
- it "does not find jobs lower than the given priority" do
170
- create_job priority: 5
171
- found = Delayed::Job.get_and_lock_next_available("test1", Delayed::Settings.queue, 10, 20)
172
- expect(found).to be_nil
173
- job2 = create_job priority: 10
174
- found = Delayed::Job.get_and_lock_next_available("test1", Delayed::Settings.queue, 10, 20)
175
- expect(found).to eq(job2)
176
- job3 = create_job priority: 15
177
- found = Delayed::Job.get_and_lock_next_available("test2", Delayed::Settings.queue, 10, 20)
178
- expect(found).to eq(job3)
179
- end
180
-
181
- it "does not find jobs higher than the given priority" do
182
- create_job priority: 25
183
- found = Delayed::Job.get_and_lock_next_available("test1", Delayed::Settings.queue, 10, 20)
184
- expect(found).to be_nil
185
- job2 = create_job priority: 20
186
- found = Delayed::Job.get_and_lock_next_available("test1", Delayed::Settings.queue, 10, 20)
187
- expect(found).to eq(job2)
188
- job3 = create_job priority: 15
189
- found = Delayed::Job.get_and_lock_next_available("test2", Delayed::Settings.queue, 10, 20)
190
- expect(found).to eq(job3)
191
- end
192
- end
193
-
194
- context "clear_locks!" do
195
- before do
196
- @job = create_job(locked_by: "worker", locked_at: Delayed::Job.db_time_now)
197
- end
198
-
199
- it "clears locks for the given worker" do
200
- Delayed::Job.clear_locks!("worker")
201
- expect(Delayed::Job.find_available(5)).to include(@job)
202
- end
203
-
204
- it "does not clear locks for other workers" do
205
- Delayed::Job.clear_locks!("worker1")
206
- expect(Delayed::Job.find_available(5)).not_to include(@job)
207
- end
208
- end
209
-
210
- context "unlock" do
211
- before do
212
- @job = create_job(locked_by: "worker", locked_at: Delayed::Job.db_time_now)
213
- end
214
-
215
- it "clears locks" do
216
- @job.unlock
217
- expect(@job.locked_by).to be_nil
218
- expect(@job.locked_at).to be_nil
219
- end
220
-
221
- it "clears locks from multiple jobs" do
222
- job2 = create_job(locked_by: "worker", locked_at: Delayed::Job.db_time_now)
223
- Delayed::Job.unlock([@job, job2])
224
- expect(@job.locked_at).to be_nil
225
- expect(job2.locked_at).to be_nil
226
- # make sure it was persisted, too
227
- expect(Delayed::Job.find(@job.id).locked_at).to be_nil
228
- end
229
- end
230
-
231
- describe "#transfer_lock" do
232
- it "works" do
233
- job = create_job(locked_by: "worker", locked_at: Delayed::Job.db_time_now)
234
- expect(job.transfer_lock!(from: "worker", to: "worker2")).to eq true
235
- expect(Delayed::Job.find(job.id).locked_by).to eq "worker2"
236
- end
237
- end
238
-
239
- context "strands" do
240
- it "runs strand jobs in strict order" do
241
- job1 = create_job(strand: "myjobs")
242
- job2 = create_job(strand: "myjobs")
243
- expect(Delayed::Job.get_and_lock_next_available("w1")).to eq(job1)
244
- expect(Delayed::Job.get_and_lock_next_available("w2")).to eq(nil)
245
- job1.destroy
246
- # update time since the failed lock pushed it forward
247
- job2.run_at = 1.minute.ago
248
- job2.save!
249
- expect(Delayed::Job.get_and_lock_next_available("w3")).to eq(job2)
250
- expect(Delayed::Job.get_and_lock_next_available("w4")).to eq(nil)
251
- end
252
-
253
- it "fails to lock if an earlier job gets locked" do
254
- job1 = create_job(strand: "myjobs")
255
- job2 = create_job(strand: "myjobs")
256
- expect(Delayed::Job.find_available(2)).to eq([job1])
257
- expect(Delayed::Job.find_available(2)).to eq([job1])
258
-
259
- # job1 gets locked by w1
260
- expect(Delayed::Job.get_and_lock_next_available("w1")).to eq(job1)
261
-
262
- # normally w2 would now be able to lock job2, but strands prevent it
263
- expect(Delayed::Job.get_and_lock_next_available("w2")).to be_nil
264
-
265
- # now job1 is done
266
- job1.destroy
267
- # update time since the failed lock pushed it forward
268
- job2.run_at = 1.minute.ago
269
- job2.save!
270
- expect(Delayed::Job.get_and_lock_next_available("w2")).to eq(job2)
271
- end
272
-
273
- it "keeps strand jobs in order as they are rescheduled" do
274
- job1 = create_job(strand: "myjobs")
275
- job2 = create_job(strand: "myjobs")
276
- job3 = create_job(strand: "myjobs")
277
- expect(Delayed::Job.get_and_lock_next_available("w1")).to eq(job1)
278
- expect(Delayed::Job.find_available(1)).to eq([])
279
- job1.destroy
280
- expect(Delayed::Job.find_available(1)).to eq([job2])
281
- # move job2's time forward
282
- job2.run_at = 1.second.ago
283
- job2.save!
284
- job3.run_at = 5.seconds.ago
285
- job3.save!
286
- # we should still get job2, not job3
287
- expect(Delayed::Job.get_and_lock_next_available("w1")).to eq(job2)
288
- end
289
-
290
- it "allows to run the next job if a failed job is present" do
291
- job1 = create_job(strand: "myjobs")
292
- job2 = create_job(strand: "myjobs")
293
- job1.fail!
294
- expect(Delayed::Job.get_and_lock_next_available("w1")).to eq(job2)
295
- end
296
-
297
- it "does not interfere with jobs with no strand" do
298
- jobs = [create_job(strand: nil), create_job(strand: "myjobs")]
299
- locked = [Delayed::Job.get_and_lock_next_available("w1"),
300
- Delayed::Job.get_and_lock_next_available("w2")]
301
- expect(jobs).to eq locked
302
- expect(Delayed::Job.get_and_lock_next_available("w3")).to eq(nil)
303
- end
304
-
305
- it "does not interfere with jobs in other strands" do
306
- jobs = [create_job(strand: "strand1"), create_job(strand: "strand2")]
307
- locked = [Delayed::Job.get_and_lock_next_available("w1"),
308
- Delayed::Job.get_and_lock_next_available("w2")]
309
- expect(jobs).to eq locked
310
- expect(Delayed::Job.get_and_lock_next_available("w3")).to eq(nil)
311
- end
312
-
313
- it "does not find next jobs when given no priority" do
314
- jobs = [create_job(strand: "strand1"), create_job(strand: "strand1")]
315
- first = Delayed::Job.get_and_lock_next_available("w1", Delayed::Settings.queue, nil, nil)
316
- second = Delayed::Job.get_and_lock_next_available("w2", Delayed::Settings.queue, nil, nil)
317
- expect(first).to eq jobs.first
318
- expect(second).to eq nil
319
- end
320
-
321
- it "complains if you pass more than one strand-based option" do
322
- expect { create_job(strand: "a", n_strand: "b") }.to raise_error(ArgumentError)
323
- end
324
-
325
- context "singleton" do
326
- it "creates if there's no jobs on the strand" do
327
- @job = create_job(singleton: "myjobs")
328
- expect(@job).to be_present
329
- expect(Delayed::Job.get_and_lock_next_available("w1")).to eq(@job)
330
- end
331
-
332
- it "creates if there's another job on the strand, but it's running" do
333
- @job = create_job(singleton: "myjobs")
334
- expect(@job).to be_present
335
- expect(Delayed::Job.get_and_lock_next_available("w1")).to eq(@job)
336
-
337
- @job2 = create_job(singleton: "myjobs")
338
- expect(@job).to be_present
339
- expect(@job2).not_to eq(@job)
340
- end
341
-
342
- it "does not create if there's another non-running job on the strand" do
343
- @job = create_job(singleton: "myjobs")
344
- expect(@job).to be_present
345
-
346
- @job2 = create_job(singleton: "myjobs")
347
- expect(@job2).to be_new_record
348
- end
349
-
350
- it "does not create if there's a job running and one waiting on the strand" do
351
- @job = create_job(singleton: "myjobs")
352
- expect(@job).to be_present
353
- expect(Delayed::Job.get_and_lock_next_available("w1")).to eq(@job)
354
-
355
- @job2 = create_job(singleton: "myjobs")
356
- expect(@job2).to be_present
357
- expect(@job2).not_to eq(@job)
358
-
359
- @job3 = create_job(singleton: "myjobs")
360
- expect(@job3).to be_new_record
361
- end
362
-
363
- it "updates existing job if new job is set to run sooner" do
364
- job1 = create_job(singleton: "myjobs", run_at: 1.hour.from_now)
365
- job2 = create_job(singleton: "myjobs")
366
- expect(job2).to eq(job1)
367
- # it should be scheduled to run immediately
368
- expect(Delayed::Job.get_and_lock_next_available("w1")).to eq(job1)
369
- end
370
-
371
- it "updates existing job to a later date if requested" do
372
- t1 = 1.hour.from_now
373
- t2 = 2.hours.from_now
374
- job1 = create_job(singleton: "myjobs", run_at: t1)
375
- job2 = create_job(singleton: "myjobs", run_at: t2)
376
- expect(job2).to be_new_record
377
-
378
- job3 = create_job(singleton: "myjobs", run_at: t2, on_conflict: :overwrite)
379
- expect(job3).to eq(job1)
380
- expect(job3.run_at.to_i).to eq(t2.to_i)
381
- end
382
-
383
- it "updates existing singleton job handler if requested" do
384
- job1 = Delayed::Job.enqueue(SimpleJob.new, queue: nil, singleton: "myjobs", on_conflict: :overwrite)
385
- job2 = Delayed::Job.enqueue(ErrorJob.new, queue: nil, singleton: "myjobs", on_conflict: :overwrite)
386
- expect(job2).to eq(job1)
387
- expect(job1.reload.handler).to include("ErrorJob")
388
- end
389
-
390
- context "next_in_strand management" do
391
- it "creates first as true, and second as false, then transitions to second when deleted" do
392
- @job1 = create_job(singleton: "myjobs")
393
- Delayed::Job.get_and_lock_next_available("w1")
394
- @job2 = create_job(singleton: "myjobs")
395
- expect(@job1.reload.next_in_strand).to eq true
396
- expect(@job2.reload.next_in_strand).to eq false
397
-
398
- @job1.destroy
399
- expect(@job2.reload.next_in_strand).to eq true
400
- end
401
-
402
- it "when combined with a strand" do
403
- job1 = create_job(singleton: "singleton", strand: "strand")
404
- expect(Delayed::Job.get_and_lock_next_available("w1")).to eq job1
405
- job2 = create_job(singleton: "singleton", strand: "strand")
406
- expect(job2).not_to eq job1
407
- expect(job2).not_to be_new_record
408
- expect(Delayed::Job.get_and_lock_next_available("w1")).to be_nil
409
- job3 = create_job(strand: "strand")
410
- job4 = create_job(strand: "strand")
411
- expect(job3.reload).not_to be_next_in_strand
412
- expect(job4.reload).not_to be_next_in_strand
413
- expect(Delayed::Job.get_and_lock_next_available("w1")).to be_nil
414
- job1.destroy
415
- expect(Delayed::Job.get_and_lock_next_available("w1")).to eq job2
416
- expect(Delayed::Job.get_and_lock_next_available("w1")).to be_nil
417
- job2.destroy
418
- expect(Delayed::Job.get_and_lock_next_available("w1")).to eq job3
419
- expect(Delayed::Job.get_and_lock_next_available("w1")).to be_nil
420
- job3.destroy
421
- expect(Delayed::Job.get_and_lock_next_available("w1")).to eq job4
422
- expect(Delayed::Job.get_and_lock_next_available("w1")).to be_nil
423
- end
424
-
425
- it "when combined with a small n_strand" do
426
- allow(Delayed::Settings).to receive(:num_strands).and_return(->(*) { 2 })
427
-
428
- job1 = create_job(singleton: "singleton", n_strand: "strand")
429
- expect(Delayed::Job.get_and_lock_next_available("w1")).to eq job1
430
- job2 = create_job(singleton: "singleton", n_strand: "strand")
431
- expect(job2).not_to eq job1
432
- expect(job2).not_to be_new_record
433
- expect(Delayed::Job.get_and_lock_next_available("w1")).to be_nil
434
- job3 = create_job(n_strand: "strand")
435
- job4 = create_job(n_strand: "strand")
436
- expect(job3.reload).to be_next_in_strand
437
- expect(job4.reload).not_to be_next_in_strand
438
- expect(Delayed::Job.get_and_lock_next_available("w1")).to eq job3
439
- expect(Delayed::Job.get_and_lock_next_available("w1")).to be_nil
440
- # this doesn't unlock job2, even though it's ahead of job4
441
- job3.destroy
442
- expect(Delayed::Job.get_and_lock_next_available("w1")).to eq job4
443
- expect(Delayed::Job.get_and_lock_next_available("w1")).to be_nil
444
- job4.destroy
445
- expect(Delayed::Job.get_and_lock_next_available("w1")).to be_nil
446
- job1.destroy
447
- expect(Delayed::Job.get_and_lock_next_available("w1")).to eq job2
448
- expect(Delayed::Job.get_and_lock_next_available("w1")).to be_nil
449
- end
450
-
451
- it "when combined with a larger n_strand" do
452
- allow(Delayed::Settings).to receive(:num_strands).and_return(->(*) { 10 })
453
-
454
- job1 = create_job(singleton: "singleton", n_strand: "strand")
455
- expect(Delayed::Job.get_and_lock_next_available("w1")).to eq job1
456
- job2 = create_job(singleton: "singleton", n_strand: "strand")
457
- expect(job2).not_to eq job1
458
- expect(job2).not_to be_new_record
459
- expect(Delayed::Job.get_and_lock_next_available("w1")).to be_nil
460
- job3 = create_job(n_strand: "strand")
461
- job4 = create_job(n_strand: "strand")
462
- expect(job3.reload).to be_next_in_strand
463
- expect(job4.reload).to be_next_in_strand
464
- expect(Delayed::Job.get_and_lock_next_available("w1")).to eq job3
465
- expect(Delayed::Job.get_and_lock_next_available("w1")).to eq job4
466
- expect(Delayed::Job.get_and_lock_next_available("w1")).to be_nil
467
- # this doesn't unlock job2
468
- job3.destroy
469
- expect(Delayed::Job.get_and_lock_next_available("w1")).to be_nil
470
- job4.destroy
471
- expect(Delayed::Job.get_and_lock_next_available("w1")).to be_nil
472
- job1.destroy
473
- expect(Delayed::Job.get_and_lock_next_available("w1")).to eq job2
474
- expect(Delayed::Job.get_and_lock_next_available("w1")).to be_nil
475
- end
476
- end
477
-
478
- context "with on_conflict: loose and strand-inferred-from-singleton" do
479
- around do |example|
480
- Delayed::Settings.infer_strand_from_singleton = true
481
- example.call
482
- ensure
483
- Delayed::Settings.infer_strand_from_singleton = false
484
- end
485
-
486
- it "does not create if there's another non-running job on the strand" do
487
- @job = create_job(singleton: "myjobs", on_conflict: :loose)
488
- expect(@job).to be_present
489
-
490
- @job2 = create_job(singleton: "myjobs", on_conflict: :loose)
491
- expect(@job2).to be_new_record
492
- end
493
- end
494
-
495
- context "when unlocking with another singleton pending" do
496
- it "deletes the pending singleton" do
497
- @job1 = create_job(singleton: "myjobs", max_attempts: 2)
498
- expect(Delayed::Job.get_and_lock_next_available("w1")).to eq(@job1)
499
-
500
- @job2 = create_job(singleton: "myjobs", max_attempts: 2)
501
-
502
- @job1.reload.reschedule
503
- expect { @job1.reload }.not_to raise_error
504
- expect { @job2.reload }.to raise_error(ActiveRecord::RecordNotFound)
505
- end
506
- end
507
- end
508
- end
509
-
510
- context "on hold" do
511
- it "hold/unholds jobs" do
512
- job1 = create_job
513
- job1.hold!
514
- expect(Delayed::Job.get_and_lock_next_available("w1")).to be_nil
515
-
516
- job1.unhold!
517
- expect(Delayed::Job.get_and_lock_next_available("w1")).to eq(job1)
518
- end
519
- end
520
-
521
- context "periodic jobs" do
522
- before do
523
- # make the periodic job get scheduled in the past
524
- @cron_time = 10.minutes.ago
525
- allow(Delayed::Periodic).to receive(:now).and_return(@cron_time)
526
- Delayed::Periodic.scheduled = {}
527
- Delayed::Periodic.cron("my SimpleJob", "*/5 * * * * *") do
528
- Delayed::Job.enqueue(SimpleJob.new)
529
- end
530
- end
531
-
532
- it "schedules jobs if they aren't scheduled yet" do
533
- expect(Delayed::Job.jobs_count(:current)).to eq(0)
534
- Delayed::Periodic.perform_audit!
535
- expect(Delayed::Job.jobs_count(:current)).to eq(1)
536
- job = Delayed::Job.get_and_lock_next_available("test1")
537
- expect(job.tag).to eq("periodic: my SimpleJob")
538
- expect(job.payload_object).to eq(Delayed::Periodic.scheduled["my SimpleJob"])
539
- expect(job.run_at).to be >= @cron_time
540
- expect(job.run_at).to be <= @cron_time + 6.minutes
541
- expect(job.singleton).to eq(job.tag)
542
- end
543
-
544
- it "schedules jobs if there are only failed jobs on the queue" do
545
- expect(Delayed::Job.jobs_count(:current)).to eq(0)
546
- expect { Delayed::Periodic.perform_audit! }.to change { Delayed::Job.jobs_count(:current) }.by(1)
547
- expect(Delayed::Job.jobs_count(:current)).to eq(1)
548
- job = Delayed::Job.get_and_lock_next_available("test1")
549
- job.fail!
550
- expect { Delayed::Periodic.perform_audit! }.to change { Delayed::Job.jobs_count(:current) }.by(1)
551
- end
552
-
553
- it "does not schedule jobs that are already scheduled" do
554
- expect(Delayed::Job.jobs_count(:current)).to eq(0)
555
- Delayed::Periodic.perform_audit!
556
- expect(Delayed::Job.jobs_count(:current)).to eq(1)
557
- job = Delayed::Job.find_available(1).first
558
- Delayed::Periodic.perform_audit!
559
- expect(Delayed::Job.jobs_count(:current)).to eq(1)
560
- # verify that the same job still exists, it wasn't just replaced with a new one
561
- expect(job).to eq(Delayed::Job.find_available(1).first)
562
- end
563
-
564
- it "schedules the next job run after performing" do
565
- expect(Delayed::Job.jobs_count(:current)).to eq(0)
566
- Delayed::Periodic.perform_audit!
567
- expect(Delayed::Job.jobs_count(:current)).to eq(1)
568
- job = Delayed::Job.get_and_lock_next_available("test")
569
- run_job(job)
570
-
571
- job = Delayed::Job.get_and_lock_next_available("test1")
572
- expect(job.tag).to eq("SimpleJob#perform")
573
-
574
- next_scheduled = Delayed::Job.get_and_lock_next_available("test2")
575
- expect(next_scheduled.tag).to eq("periodic: my SimpleJob")
576
- expect(next_scheduled.payload_object).to be_is_a(Delayed::Periodic)
577
- end
578
-
579
- it "rejects duplicate named jobs" do
580
- expect { Delayed::Periodic.cron("my SimpleJob", "*/15 * * * * *") { nil } }.to raise_error(ArgumentError)
581
- end
582
-
583
- it "handles jobs that are no longer scheduled" do
584
- Delayed::Periodic.perform_audit!
585
- Delayed::Periodic.scheduled = {}
586
- job = Delayed::Job.get_and_lock_next_available("test")
587
- run_job(job)
588
- # shouldn't error, and the job should now be deleted
589
- expect(Delayed::Job.jobs_count(:current)).to eq(0)
590
- end
591
-
592
- it "allows overriding schedules using periodic_jobs.yml" do
593
- change_setting(Delayed::Periodic, :overrides, { "my ChangedJob" => "*/10 * * * * *" }) do
594
- Delayed::Periodic.scheduled = {}
595
- Delayed::Periodic.cron("my ChangedJob", "*/5 * * * * *") do
596
- Delayed::Job.enqueue(SimpleJob.new)
597
- end
598
- expect(Delayed::Periodic.scheduled["my ChangedJob"].cron.original).to eq("*/10 * * * * *")
599
- end
600
- end
601
-
602
- it "fails if the override cron line is invalid" do
603
- change_setting(Delayed::Periodic, :overrides, { "my ChangedJob" => "*/10 * * * * * *" }) do # extra asterisk
604
- Delayed::Periodic.scheduled = {}
605
- expect do
606
- Delayed::Periodic.cron("my ChangedJob", "*/5 * * * * *") do
607
- Delayed::Job.enqueue(SimpleJob.new)
608
- end
609
- end.to raise_error(ArgumentError)
610
- end
611
-
612
- expect do
613
- Delayed::Periodic.add_overrides({ "my ChangedJob" => "*/10 * * * * * *" })
614
- end.to raise_error(ArgumentError)
615
- end
616
- end
617
-
618
- it "sets in_delayed_job?" do
619
- job = InDelayedJobTest.delay(ignore_transaction: true).check_in_job
620
- expect(Delayed::Job.in_delayed_job?).to eq(false)
621
- job.invoke_job
622
- expect(Delayed::Job.in_delayed_job?).to eq(false)
623
- end
624
-
625
- it "fails on job creation if an unsaved AR object is used" do
626
- story = Story.new text: "Once upon..."
627
- expect { story.delay.text }.to raise_error(RuntimeError)
628
-
629
- reader = StoryReader.new
630
- expect { reader.delay.read(story) }.to raise_error(RuntimeError)
631
-
632
- expect { [story, 1, story, false].delay.first }.to raise_error(RuntimeError)
633
- end
634
-
635
- # the sort order of current_jobs and list_jobs depends on the back-end
636
- # implementation, so sort order isn't tested in these specs
637
- describe "current jobs, queue size, strand_size" do
638
- before do
639
- @jobs = []
640
- 3.times { @jobs << create_job(priority: 3) }
641
- @jobs.unshift create_job(priority: 2)
642
- @jobs.unshift create_job(priority: 1)
643
- @jobs << create_job(priority: 3, strand: "test1")
644
- @future_job = create_job(run_at: 5.hours.from_now)
645
- 2.times { @jobs << create_job(priority: 3) }
646
- @jobs << create_job(priority: 3, strand: "test1")
647
- @failed_job = create_job.tap(&:fail!)
648
- @other_queue_job = create_job(queue: "another")
649
- end
650
-
651
- it "returns the queued jobs" do
652
- expect(Delayed::Job.list_jobs(:current, 100).map(&:id).sort).to eq(@jobs.map(&:id).sort)
653
- end
654
-
655
- it "paginates the returned jobs" do
656
- @returned = []
657
- @returned += Delayed::Job.list_jobs(:current, 3, 0)
658
- @returned += Delayed::Job.list_jobs(:current, 4, 3)
659
- @returned += Delayed::Job.list_jobs(:current, 100, 7)
660
- expect(@returned.sort_by(&:id)).to eq(@jobs.sort_by(&:id))
661
- end
662
-
663
- it "returns other queues" do
664
- expect(Delayed::Job.list_jobs(:current, 5, 0, "another")).to eq([@other_queue_job])
665
- end
666
-
667
- it "returns queue size" do
668
- expect(Delayed::Job.jobs_count(:current)).to eq(@jobs.size)
669
- expect(Delayed::Job.jobs_count(:current, "another")).to eq(1)
670
- expect(Delayed::Job.jobs_count(:current, "bogus")).to eq(0)
671
- end
672
-
673
- it "returns strand size" do
674
- expect(Delayed::Job.strand_size("test1")).to eq(2)
675
- expect(Delayed::Job.strand_size("bogus")).to eq(0)
676
- end
677
- end
678
-
679
- it "returns the jobs in a strand" do
680
- strand_jobs = []
681
- 3.times { strand_jobs << create_job(strand: "test1") }
682
- 2.times { create_job(strand: "test2") }
683
- strand_jobs << create_job(strand: "test1", run_at: 5.hours.from_now)
684
- create_job
685
-
686
- jobs = Delayed::Job.list_jobs(:strand, 3, 0, "test1")
687
- expect(jobs.size).to eq(3)
688
-
689
- jobs += Delayed::Job.list_jobs(:strand, 3, 3, "test1")
690
- expect(jobs.size).to eq(4)
691
-
692
- expect(jobs.sort_by(&:id)).to eq(strand_jobs.sort_by(&:id))
693
- end
694
-
695
- it "returns the jobs for a tag" do
696
- tag_jobs = []
697
- 3.times { tag_jobs << "test".delay(ignore_transaction: true).to_s }
698
- 2.times { "test".delay.to_i }
699
- tag_jobs << "test".delay(ignore_transaction: true, run_at: 5.hours.from_now).to_s
700
- tag_jobs << "test".delay(ignore_transaction: true, strand: "test1").to_s
701
- "test".delay(strand: "test1").to_i
702
- create_job
703
-
704
- jobs = Delayed::Job.list_jobs(:tag, 3, 0, "String#to_s")
705
- expect(jobs.size).to eq(3)
706
-
707
- jobs += Delayed::Job.list_jobs(:tag, 3, 3, "String#to_s")
708
- expect(jobs.size).to eq(5)
709
-
710
- expect(jobs.sort_by(&:id)).to eq(tag_jobs.sort_by(&:id))
711
- end
712
-
713
- describe "running_jobs" do
714
- it "returns the running jobs, ordered by locked_at" do
715
- Timecop.freeze(10.minutes.ago) { 3.times { create_job } }
716
- j1 = Timecop.freeze(2.minutes.ago) { Delayed::Job.get_and_lock_next_available("w1") }
717
- j2 = Timecop.freeze(5.minutes.ago) { Delayed::Job.get_and_lock_next_available("w2") }
718
- j3 = Timecop.freeze(5.seconds.ago) { Delayed::Job.get_and_lock_next_available("w3") }
719
- expect([j1, j2, j3].compact.size).to eq(3)
720
-
721
- expect(Delayed::Job.running_jobs).to eq([j2, j1, j3])
722
- end
723
- end
724
-
725
- describe "future jobs" do
726
- it "finds future jobs once their run_at rolls by" do
727
- Timecop.freeze do
728
- @job = create_job run_at: 5.minutes.from_now
729
- expect(Delayed::Job.find_available(5)).not_to include(@job)
730
- end
731
- Timecop.freeze(1.hour.from_now) do
732
- expect(Delayed::Job.find_available(5)).to include(@job)
733
- expect(Delayed::Job.get_and_lock_next_available("test")).to eq(@job)
734
- end
735
- end
736
-
737
- it "returns future jobs sorted by their run_at" do
738
- @j1 = create_job
739
- @j2 = create_job run_at: 1.hour.from_now
740
- @j3 = create_job run_at: 30.minutes.from_now
741
- expect(Delayed::Job.list_jobs(:future, 1)).to eq([@j3])
742
- expect(Delayed::Job.list_jobs(:future, 5)).to eq([@j3, @j2])
743
- expect(Delayed::Job.list_jobs(:future, 1, 1)).to eq([@j2])
744
- end
745
- end
746
-
747
- describe "failed jobs" do
748
- # the sort order of failed_jobs depends on the back-end implementation,
749
- # so sort order isn't tested here
750
- it "returns the list of failed jobs" do
751
- jobs = []
752
- 3.times { jobs << create_job(priority: 3) }
753
- jobs = jobs.sort_by(&:id)
754
- expect(Delayed::Job.list_jobs(:failed, 1)).to eq([])
755
- jobs[0].fail!
756
- jobs[1].fail!
757
- failed = (Delayed::Job.list_jobs(:failed, 1, 0) + Delayed::Job.list_jobs(:failed, 1, 1)).sort_by(&:id)
758
- expect(failed.size).to eq(2)
759
- expect(failed[0].original_job_id).to eq(jobs[0].id)
760
- expect(failed[1].original_job_id).to eq(jobs[1].id)
761
- end
762
- end
763
-
764
- describe "bulk_update" do
765
- shared_examples_for "scope" do
766
- before do
767
- @affected_jobs = []
768
- @ignored_jobs = []
769
- end
770
-
771
- it "holds and unhold a scope of jobs" do
772
- expect(@affected_jobs.all?(&:on_hold?)).to be false
773
- expect(@ignored_jobs.any?(&:on_hold?)).to be false
774
- expect(Delayed::Job.bulk_update("hold", flavor: @flavor, query: @query)).to eq(@affected_jobs.size)
775
-
776
- expect(@affected_jobs.all? { |j| Delayed::Job.find(j.id).on_hold? }).to be true
777
- expect(@ignored_jobs.any? { |j| Delayed::Job.find(j.id).on_hold? }).to be false
778
-
779
- expect(Delayed::Job.bulk_update("unhold", flavor: @flavor, query: @query)).to eq(@affected_jobs.size)
780
-
781
- expect(@affected_jobs.any? { |j| Delayed::Job.find(j.id).on_hold? }).to be false
782
- expect(@ignored_jobs.any? { |j| Delayed::Job.find(j.id).on_hold? }).to be false
783
- end
784
-
785
- it "deletes a scope of jobs" do
786
- expect(Delayed::Job.bulk_update("destroy", flavor: @flavor, query: @query)).to eq(@affected_jobs.size)
787
- expect(Delayed::Job.where(id: @affected_jobs.map(&:id))).not_to exist
788
- expect(Delayed::Job.where(id: @ignored_jobs.map(&:id)).count).to eq @ignored_jobs.size
789
- end
790
- end
791
-
792
- describe "scope: current" do
793
- include_examples "scope"
794
- before do # rubocop:disable RSpec/HooksBeforeExamples
795
- @flavor = "current"
796
- Timecop.freeze(5.minutes.ago) do
797
- 3.times { @affected_jobs << create_job }
798
- @ignored_jobs << create_job(run_at: 2.hours.from_now)
799
- @ignored_jobs << create_job(queue: "q2")
800
- end
801
- end
802
- end
803
-
804
- describe "scope: future" do
805
- include_examples "scope"
806
- before do # rubocop:disable RSpec/HooksBeforeExamples
807
- @flavor = "future"
808
- Timecop.freeze(5.minutes.ago) do
809
- 3.times { @affected_jobs << create_job(run_at: 2.hours.from_now) }
810
- @ignored_jobs << create_job
811
- @ignored_jobs << create_job(queue: "q2", run_at: 2.hours.from_now)
812
- end
813
- end
814
- end
815
-
816
- describe "scope: strand" do
817
- include_examples "scope"
818
- before do # rubocop:disable RSpec/HooksBeforeExamples
819
- @flavor = "strand"
820
- @query = "s1"
821
- Timecop.freeze(5.minutes.ago) do
822
- @affected_jobs << create_job(strand: "s1")
823
- @affected_jobs << create_job(strand: "s1", run_at: 2.hours.from_now)
824
- @ignored_jobs << create_job
825
- @ignored_jobs << create_job(strand: "s2")
826
- @ignored_jobs << create_job(strand: "s2", run_at: 2.hours.from_now)
827
- end
828
- end
829
- end
830
-
831
- describe "scope: tag" do
832
- include_examples "scope"
833
- before do # rubocop:disable RSpec/HooksBeforeExamples
834
- @flavor = "tag"
835
- @query = "String#to_i"
836
- Timecop.freeze(5.minutes.ago) do
837
- @affected_jobs << "test".delay(ignore_transaction: true).to_i
838
- @affected_jobs << "test".delay(strand: "s1", ignore_transaction: true).to_i
839
- @affected_jobs << "test".delay(run_at: 2.hours.from_now, ignore_transaction: true).to_i
840
- @ignored_jobs << create_job
841
- @ignored_jobs << create_job(run_at: 1.hour.from_now)
842
- end
843
- end
844
- end
845
-
846
- it "holds and un-hold given job ids" do
847
- j1 = "test".delay(ignore_transaction: true).to_i
848
- j2 = create_job(run_at: 2.hours.from_now)
849
- j3 = "test".delay(strand: "s1", ignore_transaction: true).to_i
850
- expect(Delayed::Job.bulk_update("hold", ids: [j1.id, j2.id])).to eq(2)
851
- expect(Delayed::Job.find(j1.id).on_hold?).to be true
852
- expect(Delayed::Job.find(j2.id).on_hold?).to be true
853
- expect(Delayed::Job.find(j3.id).on_hold?).to be false
854
-
855
- expect(Delayed::Job.bulk_update("unhold", ids: [j2.id, j3.id])).to eq(1)
856
- expect(Delayed::Job.find(j1.id).on_hold?).to be true
857
- expect(Delayed::Job.find(j2.id).on_hold?).to be false
858
- expect(Delayed::Job.find(j3.id).on_hold?).to be false
859
- end
860
-
861
- it "does not hold locked jobs" do
862
- job1 = Delayed::Job.new(tag: "tag")
863
- job1.create_and_lock!("worker")
864
- expect(job1.on_hold?).to be false
865
- expect(Delayed::Job.bulk_update("hold", ids: [job1.id])).to eq(0)
866
- expect(Delayed::Job.find(job1.id).on_hold?).to be false
867
- end
868
-
869
- it "does not unhold locked jobs" do
870
- job1 = Delayed::Job.new(tag: "tag")
871
- job1.create_and_lock!("worker")
872
- expect(Delayed::Job.bulk_update("unhold", ids: [job1.id])).to eq(0)
873
- expect(Delayed::Job.find(job1.id).on_hold?).to be false
874
- expect(Delayed::Job.find(job1.id).locked?).to be true
875
- end
876
-
877
- it "deletes given job ids" do
878
- jobs = (0..2).map { create_job }
879
- expect(Delayed::Job.bulk_update("destroy", ids: jobs[0, 2].map(&:id))).to eq(2)
880
- expect(Delayed::Job.order(:id).where(id: jobs.map(&:id))).to eq jobs[2, 1]
881
- end
882
-
883
- it "does not delete locked jobs" do
884
- job1 = Delayed::Job.new(tag: "tag")
885
- job1.create_and_lock!("worker")
886
- expect(Delayed::Job.bulk_update("destroy", ids: [job1.id])).to eq(0)
887
- expect(Delayed::Job.find(job1.id).locked?).to be true
888
- end
889
- end
890
-
891
- describe "tag_counts" do
892
- before do
893
- @cur = []
894
- 3.times { @cur << "test".delay(ignore_transaction: true).to_s }
895
- 5.times { @cur << "test".delay(ignore_transaction: true).to_i }
896
- 2.times { @cur << "test".delay(ignore_transaction: true).upcase }
897
- "test".delay(ignore_transaction: true).downcase.fail!
898
- @future = []
899
- 5.times { @future << "test".delay(run_at: 3.hours.from_now, ignore_transaction: true).downcase }
900
- @cur << "test".delay(ignore_transaction: true).downcase
901
- end
902
-
903
- it "returns a sorted list of popular current tags" do
904
- expect(Delayed::Job.tag_counts(:current, 1)).to eq([{ tag: "String#to_i", count: 5 }])
905
- expect(Delayed::Job.tag_counts(:current, 1, 1)).to eq([{ tag: "String#to_s", count: 3 }])
906
- expect(Delayed::Job.tag_counts(:current, 5)).to eq([{ tag: "String#to_i", count: 5 },
907
- { tag: "String#to_s", count: 3 },
908
- { tag: "String#upcase", count: 2 },
909
- { tag: "String#downcase", count: 1 }])
910
- @cur[0, 4].each(&:destroy)
911
- @future[0].run_at = @future[1].run_at = 1.hour.ago
912
- @future[0].save!
913
- @future[1].save!
914
-
915
- expect(Delayed::Job.tag_counts(:current, 5)).to eq([{ tag: "String#to_i", count: 4 },
916
- { tag: "String#downcase", count: 3 },
917
- { tag: "String#upcase", count: 2 }])
918
- end
919
-
920
- it "returns a sorted list of all popular tags" do
921
- expect(Delayed::Job.tag_counts(:all, 1)).to eq([{ tag: "String#downcase", count: 6 }])
922
- expect(Delayed::Job.tag_counts(:all, 1, 1)).to eq([{ tag: "String#to_i", count: 5 }])
923
- expect(Delayed::Job.tag_counts(:all, 5)).to eq([{ tag: "String#downcase", count: 6 },
924
- { tag: "String#to_i", count: 5 },
925
- { tag: "String#to_s", count: 3 },
926
- { tag: "String#upcase", count: 2 }])
927
-
928
- @cur[0, 4].each(&:destroy)
929
- @future[0].destroy
930
- @future[1].fail!
931
- @future[2].fail!
932
-
933
- expect(Delayed::Job.tag_counts(:all, 5)).to eq([{ tag: "String#to_i", count: 4 },
934
- { tag: "String#downcase", count: 3 },
935
- { tag: "String#upcase", count: 2 }])
936
- end
937
- end
938
-
939
- it "unlocks orphaned jobs" do
940
- change_setting(Delayed::Settings, :max_attempts, 2) do
941
- job1 = Delayed::Job.new(tag: "tag")
942
- job2 = Delayed::Job.new(tag: "tag")
943
- job3 = Delayed::Job.new(tag: "tag")
944
- job4 = Delayed::Job.new(tag: "tag")
945
- job1.create_and_lock!("Jobworker:#{Process.pid}")
946
- `echo ''`
947
- child_pid = $?.pid
948
- job2.create_and_lock!("Jobworker:#{child_pid}")
949
- job3.create_and_lock!("someoneelse:#{Process.pid}")
950
- job4.create_and_lock!("Jobworker:notanumber")
951
-
952
- expect(Delayed::Job.unlock_orphaned_jobs(nil, "Jobworker")).to eq(1)
953
-
954
- expect(Delayed::Job.find(job1.id).locked_by).not_to be_nil
955
- expect(Delayed::Job.find(job2.id).locked_by).to be_nil
956
- expect(Delayed::Job.find(job3.id).locked_by).not_to be_nil
957
- expect(Delayed::Job.find(job4.id).locked_by).not_to be_nil
958
-
959
- expect(Delayed::Job.unlock_orphaned_jobs(nil, "Jobworker")).to eq(0)
960
- end
961
- end
962
-
963
- it "unlocks orphaned jobs given a pid" do
964
- change_setting(Delayed::Settings, :max_attempts, 2) do
965
- job1 = Delayed::Job.new(tag: "tag")
966
- job2 = Delayed::Job.new(tag: "tag")
967
- job3 = Delayed::Job.new(tag: "tag")
968
- job4 = Delayed::Job.new(tag: "tag")
969
- job1.create_and_lock!("Jobworker:#{Process.pid}")
970
- `echo ''`
971
- child_pid = $?.pid
972
- `echo ''`
973
- child_pid2 = $?.pid
974
- job2.create_and_lock!("Jobworker:#{child_pid}")
975
- job3.create_and_lock!("someoneelse:#{Process.pid}")
976
- job4.create_and_lock!("Jobworker:notanumber")
977
-
978
- expect(Delayed::Job.unlock_orphaned_jobs(child_pid2, "Jobworker")).to eq(0)
979
- expect(Delayed::Job.unlock_orphaned_jobs(child_pid, "Jobworker")).to eq(1)
980
-
981
- expect(Delayed::Job.find(job1.id).locked_by).not_to be_nil
982
- expect(Delayed::Job.find(job2.id).locked_by).to be_nil
983
- expect(Delayed::Job.find(job3.id).locked_by).not_to be_nil
984
- expect(Delayed::Job.find(job4.id).locked_by).not_to be_nil
985
-
986
- expect(Delayed::Job.unlock_orphaned_jobs(child_pid, "Jobworker")).to eq(0)
987
- end
988
- end
989
- end