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