delayed_job 3.0.3 → 3.0.4
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.
- data/README.textile +1 -16
- data/contrib/delayed_job_multiple.monitrc +11 -0
- data/lib/delayed/backend/base.rb +2 -1
- data/lib/delayed/backend/shared_spec.rb +149 -169
- data/lib/delayed/performable_method.rb +2 -2
- data/lib/delayed/psych_ext.rb +1 -5
- data/lib/delayed/recipes.rb +7 -3
- data/lib/delayed/serialization/active_record.rb +1 -5
- data/lib/delayed/worker.rb +5 -2
- data/spec/lifecycle_spec.rb +5 -5
- data/spec/message_sending_spec.rb +45 -38
- data/spec/performable_mailer_spec.rb +10 -10
- data/spec/performable_method_spec.rb +17 -17
- data/spec/spec_helper.rb +4 -0
- data/spec/test_backend_spec.rb +2 -2
- data/spec/worker_spec.rb +25 -4
- data/spec/yaml_ext_spec.rb +18 -24
- metadata +6 -7
- data/spec/fixtures/bad_alias.yml +0 -1
data/README.textile
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
h1. Delayed::Job "!
|
1
|
+
h1. Delayed::Job "!https://travis-ci.org/collectiveidea/delayed_job.png!":http://travis-ci.org/collectiveidea/delayed_job "!https://gemnasium.com/collectiveidea/delayed_job.png?travis!":https://gemnasium.com/collectiveidea/delayed_job
|
2
2
|
|
3
3
|
Delayed_job (or DJ) encapsulates the common pattern of asynchronously executing longer tasks in the background.
|
4
4
|
|
@@ -275,18 +275,3 @@ You can invoke @rake jobs:clear@ to delete all jobs in the queue.
|
|
275
275
|
h2. Mailing List
|
276
276
|
|
277
277
|
Join us on the "mailing list":http://groups.google.com/group/delayed_job
|
278
|
-
|
279
|
-
h2. How to contribute
|
280
|
-
|
281
|
-
If you find what looks like a bug:
|
282
|
-
|
283
|
-
# Search the "mailing list":http://groups.google.com/group/delayed_job to see if anyone else had the same issue.
|
284
|
-
# Check the "GitHub issue tracker":http://github.com/collectiveidea/delayed_job/issues/ to see if anyone else has reported issue.
|
285
|
-
# If you don't see anything, create an issue with information on how to reproduce it.
|
286
|
-
|
287
|
-
If you want to contribute an enhancement or a fix:
|
288
|
-
|
289
|
-
# Fork the project on github.
|
290
|
-
# Make your changes with tests.
|
291
|
-
# Commit the changes without making changes to the Rakefile or any other files that aren't related to your enhancement or fix
|
292
|
-
# Send a pull request.
|
@@ -3,21 +3,32 @@
|
|
3
3
|
# To use:
|
4
4
|
# 1. copy to /var/www/apps/{app_name}/shared/delayed_job.monitrc
|
5
5
|
# 2. replace {app_name} as appropriate
|
6
|
+
# you might also need to change the program strings to
|
7
|
+
# "/bin/su - {username} -c '/usr/bin/env ...'"
|
8
|
+
# to load your shell environment.
|
9
|
+
#
|
6
10
|
# 3. add this to your /etc/monit/monitrc
|
7
11
|
#
|
8
12
|
# include /var/www/apps/{app_name}/shared/delayed_job.monitrc
|
13
|
+
#
|
14
|
+
# The processes are grouped so that monit can act on them as a whole, e.g.
|
15
|
+
#
|
16
|
+
# monit -g delayed_job restart
|
9
17
|
|
10
18
|
check process delayed_job_0
|
11
19
|
with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.0.pid
|
12
20
|
start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job start -i 0"
|
13
21
|
stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job stop -i 0"
|
22
|
+
group delayed_job
|
14
23
|
|
15
24
|
check process delayed_job_1
|
16
25
|
with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.1.pid
|
17
26
|
start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job start -i 1"
|
18
27
|
stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job stop -i 1"
|
28
|
+
group delayed_job
|
19
29
|
|
20
30
|
check process delayed_job_2
|
21
31
|
with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.2.pid
|
22
32
|
start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job start -i 2"
|
23
33
|
stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job stop -i 2"
|
34
|
+
group delayed_job
|
data/lib/delayed/backend/base.rb
CHANGED
@@ -9,7 +9,8 @@ module Delayed
|
|
9
9
|
# Add a job to the queue
|
10
10
|
def enqueue(*args)
|
11
11
|
options = {
|
12
|
-
:priority => Delayed::Worker.default_priority
|
12
|
+
:priority => Delayed::Worker.default_priority,
|
13
|
+
:queue => Delayed::Worker.default_queue_name
|
13
14
|
}.merge!(args.extract_options!)
|
14
15
|
|
15
16
|
options[:payload_object] ||= args.shift
|
@@ -2,7 +2,7 @@ require File.expand_path('../../../../spec/sample_jobs', __FILE__)
|
|
2
2
|
|
3
3
|
require 'active_support/core_ext'
|
4
4
|
|
5
|
-
shared_examples_for
|
5
|
+
shared_examples_for "a delayed_job backend" do
|
6
6
|
let(:worker) { Delayed::Worker.new }
|
7
7
|
|
8
8
|
def create_job(opts = {})
|
@@ -18,85 +18,85 @@ shared_examples_for 'a delayed_job backend' do
|
|
18
18
|
described_class.delete_all
|
19
19
|
end
|
20
20
|
|
21
|
-
it "
|
22
|
-
described_class.create(:payload_object => ErrorJob.new ).run_at.
|
21
|
+
it "sets run_at automatically if not set" do
|
22
|
+
expect(described_class.create(:payload_object => ErrorJob.new ).run_at).not_to be_nil
|
23
23
|
end
|
24
24
|
|
25
|
-
it "
|
25
|
+
it "does not set run_at automatically if already set" do
|
26
26
|
later = described_class.db_time_now + 5.minutes
|
27
27
|
job = described_class.create(:payload_object => ErrorJob.new, :run_at => later)
|
28
|
-
job.run_at.
|
28
|
+
expect(job.run_at).to be_within(1).of(later)
|
29
29
|
end
|
30
30
|
|
31
31
|
describe "#reload" do
|
32
|
-
it
|
32
|
+
it "reloads the payload" do
|
33
33
|
job = described_class.enqueue :payload_object => SimpleJob.new
|
34
|
-
job.payload_object.object_id.
|
34
|
+
expect(job.payload_object.object_id).not_to eq(job.reload.payload_object.object_id)
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
38
|
describe "enqueue" do
|
39
39
|
context "with a hash" do
|
40
|
-
it "
|
41
|
-
|
40
|
+
it "raises ArgumentError when handler doesn't respond_to :perform" do
|
41
|
+
expect{described_class.enqueue(:payload_object => Object.new)}.to raise_error(ArgumentError)
|
42
42
|
end
|
43
43
|
|
44
|
-
it "
|
44
|
+
it "is able to set priority" do
|
45
45
|
job = described_class.enqueue :payload_object => SimpleJob.new, :priority => 5
|
46
|
-
job.priority.
|
46
|
+
expect(job.priority).to eq(5)
|
47
47
|
end
|
48
48
|
|
49
|
-
it "
|
49
|
+
it "uses default priority" do
|
50
50
|
job = described_class.enqueue :payload_object => SimpleJob.new
|
51
|
-
job.priority.
|
51
|
+
expect(job.priority).to eq(99)
|
52
52
|
end
|
53
53
|
|
54
|
-
it "
|
54
|
+
it "is able to set run_at" do
|
55
55
|
later = described_class.db_time_now + 5.minutes
|
56
56
|
job = described_class.enqueue :payload_object => SimpleJob.new, :run_at => later
|
57
|
-
job.run_at.
|
57
|
+
expect(job.run_at).to be_within(1).of(later)
|
58
58
|
end
|
59
59
|
|
60
|
-
it "
|
60
|
+
it "is able to set queue" do
|
61
61
|
job = described_class.enqueue :payload_object => SimpleJob.new, :queue => 'tracking'
|
62
|
-
job.queue.
|
62
|
+
expect(job.queue).to eq('tracking')
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
66
|
context "with multiple arguments" do
|
67
|
-
it "
|
68
|
-
|
67
|
+
it "raises ArgumentError when handler doesn't respond_to :perform" do
|
68
|
+
expect{described_class.enqueue(Object.new)}.to raise_error(ArgumentError)
|
69
69
|
end
|
70
70
|
|
71
|
-
it "
|
71
|
+
it "increases count after enqueuing items" do
|
72
72
|
described_class.enqueue SimpleJob.new
|
73
|
-
described_class.count.
|
73
|
+
expect(described_class.count).to eq(1)
|
74
74
|
end
|
75
75
|
|
76
|
-
it "
|
76
|
+
it "is able to set priority [DEPRECATED]" do
|
77
77
|
silence_warnings do
|
78
78
|
job = described_class.enqueue SimpleJob.new, 5
|
79
|
-
job.priority.
|
79
|
+
expect(job.priority).to eq(5)
|
80
80
|
end
|
81
81
|
end
|
82
82
|
|
83
|
-
it "
|
83
|
+
it "uses default priority when it is not set" do
|
84
84
|
@job = described_class.enqueue SimpleJob.new
|
85
|
-
@job.priority.
|
85
|
+
expect(@job.priority).to eq(99)
|
86
86
|
end
|
87
87
|
|
88
|
-
it "
|
88
|
+
it "is able to set run_at [DEPRECATED]" do
|
89
89
|
silence_warnings do
|
90
90
|
later = described_class.db_time_now + 5.minutes
|
91
91
|
@job = described_class.enqueue SimpleJob.new, 5, later
|
92
|
-
@job.run_at.
|
92
|
+
expect(@job.run_at).to be_within(1).of(later)
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
96
|
-
it "
|
96
|
+
it "works with jobs in modules" do
|
97
97
|
M::ModuleJob.runs = 0
|
98
98
|
job = described_class.enqueue M::ModuleJob.new
|
99
|
-
|
99
|
+
expect{job.invoke_job}.to change { M::ModuleJob.runs }.from(0).to(1)
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
@@ -105,19 +105,19 @@ shared_examples_for 'a delayed_job backend' do
|
|
105
105
|
Delayed::Worker.delay_jobs = false
|
106
106
|
end
|
107
107
|
|
108
|
-
it "
|
108
|
+
it "does not increase count after enqueuing items" do
|
109
109
|
described_class.enqueue SimpleJob.new
|
110
|
-
described_class.count.
|
110
|
+
expect(described_class.count).to eq(0)
|
111
111
|
end
|
112
112
|
|
113
|
-
it
|
113
|
+
it "invokes the enqueued job" do
|
114
114
|
job = SimpleJob.new
|
115
115
|
job.should_receive(:perform)
|
116
116
|
described_class.enqueue job
|
117
117
|
end
|
118
118
|
|
119
|
-
it
|
120
|
-
described_class.enqueue(SimpleJob.new).
|
119
|
+
it "returns a job, not the result of invocation" do
|
120
|
+
expect(described_class.enqueue(SimpleJob.new)).to be_instance_of(described_class)
|
121
121
|
end
|
122
122
|
end
|
123
123
|
end
|
@@ -128,51 +128,51 @@ shared_examples_for 'a delayed_job backend' do
|
|
128
128
|
end
|
129
129
|
|
130
130
|
%w(before success after).each do |callback|
|
131
|
-
it "
|
131
|
+
it "calls #{callback} with job" do
|
132
132
|
job = described_class.enqueue(CallbackJob.new)
|
133
133
|
job.payload_object.should_receive(callback).with(job)
|
134
134
|
job.invoke_job
|
135
135
|
end
|
136
136
|
end
|
137
137
|
|
138
|
-
it "
|
138
|
+
it "calls before and after callbacks" do
|
139
139
|
job = described_class.enqueue(CallbackJob.new)
|
140
|
-
CallbackJob.messages.
|
140
|
+
expect(CallbackJob.messages).to eq(["enqueue"])
|
141
141
|
job.invoke_job
|
142
|
-
CallbackJob.messages.
|
142
|
+
expect(CallbackJob.messages).to eq(["enqueue", "before", "perform", "success", "after"])
|
143
143
|
end
|
144
144
|
|
145
|
-
it "
|
145
|
+
it "calls the after callback with an error" do
|
146
146
|
job = described_class.enqueue(CallbackJob.new)
|
147
147
|
job.payload_object.should_receive(:perform).and_raise(RuntimeError.new("fail"))
|
148
148
|
|
149
|
-
|
150
|
-
CallbackJob.messages.
|
149
|
+
expect{job.invoke_job}.to raise_error
|
150
|
+
expect(CallbackJob.messages).to eq(["enqueue", "before", "error: RuntimeError", "after"])
|
151
151
|
end
|
152
152
|
|
153
|
-
it "
|
153
|
+
it "calls error when before raises an error" do
|
154
154
|
job = described_class.enqueue(CallbackJob.new)
|
155
155
|
job.payload_object.should_receive(:before).and_raise(RuntimeError.new("fail"))
|
156
|
-
|
157
|
-
CallbackJob.messages.
|
156
|
+
expect{job.invoke_job}.to raise_error(RuntimeError)
|
157
|
+
expect(CallbackJob.messages).to eq(["enqueue", "error: RuntimeError", "after"])
|
158
158
|
end
|
159
159
|
end
|
160
160
|
|
161
161
|
describe "payload_object" do
|
162
|
-
it "
|
162
|
+
it "raises a DeserializationError when the job class is totally unknown" do
|
163
163
|
job = described_class.new :handler => "--- !ruby/object:JobThatDoesNotExist {}"
|
164
|
-
|
164
|
+
expect{job.payload_object}.to raise_error(Delayed::DeserializationError)
|
165
165
|
end
|
166
166
|
|
167
|
-
it "
|
167
|
+
it "raises a DeserializationError when the job struct is totally unknown" do
|
168
168
|
job = described_class.new :handler => "--- !ruby/struct:StructThatDoesNotExist {}"
|
169
|
-
|
169
|
+
expect{job.payload_object}.to raise_error(Delayed::DeserializationError)
|
170
170
|
end
|
171
171
|
|
172
|
-
it "
|
172
|
+
it "raises a DeserializationError when the YAML.load raises argument error" do
|
173
173
|
job = described_class.new :handler => "--- !ruby/struct:GoingToRaiseArgError {}"
|
174
174
|
YAML.should_receive(:load).and_raise(ArgumentError)
|
175
|
-
|
175
|
+
expect{job.payload_object}.to raise_error(Delayed::DeserializationError)
|
176
176
|
end
|
177
177
|
end
|
178
178
|
|
@@ -181,70 +181,70 @@ shared_examples_for 'a delayed_job backend' do
|
|
181
181
|
Delayed::Worker.max_run_time = 2.minutes
|
182
182
|
end
|
183
183
|
|
184
|
-
it "
|
184
|
+
it "does not reserve failed jobs" do
|
185
185
|
create_job :attempts => 50, :failed_at => described_class.db_time_now
|
186
|
-
described_class.reserve(worker).
|
186
|
+
expect(described_class.reserve(worker)).to be_nil
|
187
187
|
end
|
188
188
|
|
189
|
-
it "
|
189
|
+
it "does not reserve jobs scheduled for the future" do
|
190
190
|
create_job :run_at => described_class.db_time_now + 1.minute
|
191
|
-
described_class.reserve(worker).
|
191
|
+
expect(described_class.reserve(worker)).to be_nil
|
192
192
|
end
|
193
193
|
|
194
|
-
it "
|
194
|
+
it "reserves jobs scheduled for the past" do
|
195
195
|
job = create_job :run_at => described_class.db_time_now - 1.minute
|
196
|
-
described_class.reserve(worker).
|
196
|
+
expect(described_class.reserve(worker)).to eq(job)
|
197
197
|
end
|
198
198
|
|
199
|
-
it "
|
199
|
+
it "reserves jobs scheduled for the past when time zones are involved" do
|
200
200
|
Time.zone = 'US/Eastern'
|
201
201
|
job = create_job :run_at => described_class.db_time_now - 1.minute
|
202
|
-
described_class.reserve(worker).
|
202
|
+
expect(described_class.reserve(worker)).to eq(job)
|
203
203
|
end
|
204
204
|
|
205
|
-
it "
|
205
|
+
it "does not reserve jobs locked by other workers" do
|
206
206
|
job = create_job
|
207
207
|
other_worker = Delayed::Worker.new
|
208
208
|
other_worker.name = 'other_worker'
|
209
|
-
described_class.reserve(other_worker).
|
210
|
-
described_class.reserve(worker).
|
209
|
+
expect(described_class.reserve(other_worker)).to eq(job)
|
210
|
+
expect(described_class.reserve(worker)).to be_nil
|
211
211
|
end
|
212
212
|
|
213
|
-
it "
|
213
|
+
it "reserves open jobs" do
|
214
214
|
job = create_job
|
215
|
-
described_class.reserve(worker).
|
215
|
+
expect(described_class.reserve(worker)).to eq(job)
|
216
216
|
end
|
217
217
|
|
218
|
-
it "
|
218
|
+
it "reserves expired jobs" do
|
219
219
|
job = create_job(:locked_by => 'some other worker', :locked_at => described_class.db_time_now - Delayed::Worker.max_run_time - 1.minute)
|
220
|
-
described_class.reserve(worker).
|
220
|
+
expect(described_class.reserve(worker)).to eq(job)
|
221
221
|
end
|
222
222
|
|
223
|
-
it "
|
223
|
+
it "reserves own jobs" do
|
224
224
|
job = create_job(:locked_by => worker.name, :locked_at => (described_class.db_time_now - 1.minutes))
|
225
|
-
described_class.reserve(worker).
|
225
|
+
expect(described_class.reserve(worker)).to eq(job)
|
226
226
|
end
|
227
227
|
end
|
228
228
|
|
229
229
|
context "#name" do
|
230
|
-
it "
|
231
|
-
described_class.create(:payload_object => ErrorJob.new ).name.
|
230
|
+
it "is the class name of the job that was enqueued" do
|
231
|
+
expect(described_class.create(:payload_object => ErrorJob.new ).name).to eq('ErrorJob')
|
232
232
|
end
|
233
233
|
|
234
|
-
it "
|
234
|
+
it "is the method that will be called if its a performable method object" do
|
235
235
|
job = described_class.new(:payload_object => NamedJob.new)
|
236
|
-
job.name.
|
236
|
+
expect(job.name).to eq('named_job')
|
237
237
|
end
|
238
238
|
|
239
|
-
it "
|
239
|
+
it "is the instance method that will be called if its a performable method object" do
|
240
240
|
job = Story.create(:text => "...").delay.save
|
241
|
-
job.name.
|
241
|
+
expect(job.name).to eq('Story#save')
|
242
242
|
end
|
243
243
|
|
244
|
-
it "
|
244
|
+
it "parses from handler on deserialization error" do
|
245
245
|
job = Story.create(:text => "...").delay.text
|
246
246
|
job.payload_object.object.destroy
|
247
|
-
job.reload.name.
|
247
|
+
expect(job.reload.name).to eq('Delayed::PerformableMethod')
|
248
248
|
end
|
249
249
|
end
|
250
250
|
|
@@ -254,49 +254,28 @@ shared_examples_for 'a delayed_job backend' do
|
|
254
254
|
Delayed::Worker.min_priority = nil
|
255
255
|
end
|
256
256
|
|
257
|
-
it "
|
257
|
+
it "fetches jobs ordered by priority" do
|
258
258
|
10.times { described_class.enqueue SimpleJob.new, :priority => rand(10) }
|
259
259
|
jobs = []
|
260
260
|
10.times { jobs << described_class.reserve(worker) }
|
261
|
-
jobs.size.
|
261
|
+
expect(jobs.size).to eq(10)
|
262
262
|
jobs.each_cons(2) do |a, b|
|
263
|
-
a.priority.
|
263
|
+
expect(a.priority).to be <= b.priority
|
264
264
|
end
|
265
265
|
end
|
266
266
|
|
267
|
-
it "
|
267
|
+
it "only finds jobs greater than or equal to min priority" do
|
268
268
|
min = 5
|
269
269
|
Delayed::Worker.min_priority = min
|
270
270
|
10.times {|i| described_class.enqueue SimpleJob.new, :priority => i }
|
271
|
-
5.times { described_class.reserve(worker).priority.
|
271
|
+
5.times { expect(described_class.reserve(worker).priority).to be >= min }
|
272
272
|
end
|
273
273
|
|
274
|
-
it "
|
274
|
+
it "only finds jobs less than or equal to max priority" do
|
275
275
|
max = 5
|
276
276
|
Delayed::Worker.max_priority = max
|
277
277
|
10.times {|i| described_class.enqueue SimpleJob.new, :priority => i }
|
278
|
-
5.times { described_class.reserve(worker).priority.
|
279
|
-
end
|
280
|
-
end
|
281
|
-
|
282
|
-
context "worker read-ahead" do
|
283
|
-
before do
|
284
|
-
@read_ahead = Delayed::Worker.read_ahead
|
285
|
-
end
|
286
|
-
|
287
|
-
after do
|
288
|
-
Delayed::Worker.read_ahead = @read_ahead
|
289
|
-
end
|
290
|
-
|
291
|
-
it "should read five jobs" do
|
292
|
-
described_class.should_receive(:find_available).with(anything, 5, anything).and_return([])
|
293
|
-
described_class.reserve(worker)
|
294
|
-
end
|
295
|
-
|
296
|
-
it "should read a configurable number of jobs" do
|
297
|
-
Delayed::Worker.read_ahead = 15
|
298
|
-
described_class.should_receive(:find_available).with(anything, Delayed::Worker.read_ahead, anything).and_return([])
|
299
|
-
described_class.reserve(worker)
|
278
|
+
5.times { expect(described_class.reserve(worker).priority).to be <= max }
|
300
279
|
end
|
301
280
|
end
|
302
281
|
|
@@ -305,14 +284,14 @@ shared_examples_for 'a delayed_job backend' do
|
|
305
284
|
@job = create_job(:locked_by => 'worker1', :locked_at => described_class.db_time_now)
|
306
285
|
end
|
307
286
|
|
308
|
-
it "
|
287
|
+
it "clears locks for the given worker" do
|
309
288
|
described_class.clear_locks!('worker1')
|
310
|
-
described_class.reserve(worker).
|
289
|
+
expect(described_class.reserve(worker)).to eq(@job)
|
311
290
|
end
|
312
291
|
|
313
|
-
it "
|
292
|
+
it "does not clear locks for other workers" do
|
314
293
|
described_class.clear_locks!('different_worker')
|
315
|
-
described_class.reserve(worker).
|
294
|
+
expect(described_class.reserve(worker)).not_to eq(@job)
|
316
295
|
end
|
317
296
|
end
|
318
297
|
|
@@ -321,10 +300,10 @@ shared_examples_for 'a delayed_job backend' do
|
|
321
300
|
@job = create_job(:locked_by => 'worker', :locked_at => described_class.db_time_now)
|
322
301
|
end
|
323
302
|
|
324
|
-
it "
|
303
|
+
it "clears locks" do
|
325
304
|
@job.unlock
|
326
|
-
@job.locked_by.
|
327
|
-
@job.locked_at.
|
305
|
+
expect(@job.locked_by).to be_nil
|
306
|
+
expect(@job.locked_at).to be_nil
|
328
307
|
end
|
329
308
|
end
|
330
309
|
|
@@ -334,8 +313,8 @@ shared_examples_for 'a delayed_job backend' do
|
|
334
313
|
@job = described_class.enqueue Delayed::PerformableMethod.new(text, :length, {})
|
335
314
|
end
|
336
315
|
|
337
|
-
it "
|
338
|
-
@job.id.
|
316
|
+
it "has an id" do
|
317
|
+
expect(@job.id).not_to be_nil
|
339
318
|
end
|
340
319
|
end
|
341
320
|
|
@@ -345,14 +324,14 @@ shared_examples_for 'a delayed_job backend' do
|
|
345
324
|
worker.queues = ['large']
|
346
325
|
end
|
347
326
|
|
348
|
-
it "
|
349
|
-
SimpleJob.runs.
|
327
|
+
it "only works off jobs which are from its queue" do
|
328
|
+
expect(SimpleJob.runs).to eq(0)
|
350
329
|
|
351
330
|
create_job(:queue => "large")
|
352
331
|
create_job(:queue => "small")
|
353
332
|
worker.work_off
|
354
333
|
|
355
|
-
SimpleJob.runs.
|
334
|
+
expect(SimpleJob.runs).to eq(1)
|
356
335
|
end
|
357
336
|
end
|
358
337
|
|
@@ -361,8 +340,8 @@ shared_examples_for 'a delayed_job backend' do
|
|
361
340
|
worker.queues = ['large', 'small']
|
362
341
|
end
|
363
342
|
|
364
|
-
it "
|
365
|
-
SimpleJob.runs.
|
343
|
+
it "only works off jobs which are from its queue" do
|
344
|
+
expect(SimpleJob.runs).to eq(0)
|
366
345
|
|
367
346
|
create_job(:queue => "large")
|
368
347
|
create_job(:queue => "small")
|
@@ -370,7 +349,7 @@ shared_examples_for 'a delayed_job backend' do
|
|
370
349
|
create_job
|
371
350
|
worker.work_off
|
372
351
|
|
373
|
-
SimpleJob.runs.
|
352
|
+
expect(SimpleJob.runs).to eq(2)
|
374
353
|
end
|
375
354
|
end
|
376
355
|
|
@@ -379,15 +358,15 @@ shared_examples_for 'a delayed_job backend' do
|
|
379
358
|
worker.queues = []
|
380
359
|
end
|
381
360
|
|
382
|
-
it "
|
383
|
-
SimpleJob.runs.
|
361
|
+
it "works off all jobs" do
|
362
|
+
expect(SimpleJob.runs).to eq(0)
|
384
363
|
|
385
364
|
create_job(:queue => "one")
|
386
365
|
create_job(:queue => "two")
|
387
366
|
create_job
|
388
367
|
worker.work_off
|
389
368
|
|
390
|
-
SimpleJob.runs.
|
369
|
+
expect(SimpleJob.runs).to eq(3)
|
391
370
|
end
|
392
371
|
end
|
393
372
|
end
|
@@ -397,39 +376,40 @@ shared_examples_for 'a delayed_job backend' do
|
|
397
376
|
@job = described_class.enqueue SimpleJob.new
|
398
377
|
end
|
399
378
|
|
400
|
-
it
|
401
|
-
@job.max_attempts.
|
379
|
+
it "is not defined" do
|
380
|
+
expect(@job.max_attempts).to be_nil
|
402
381
|
end
|
403
382
|
|
404
|
-
it
|
383
|
+
it "uses the max_retries value on the payload when defined" do
|
405
384
|
@job.payload_object.stub!(:max_attempts).and_return(99)
|
406
|
-
@job.max_attempts.
|
385
|
+
expect(@job.max_attempts).to eq(99)
|
407
386
|
end
|
408
387
|
end
|
409
388
|
|
410
389
|
describe "yaml serialization" do
|
411
|
-
it "
|
390
|
+
it "reloads changed attributes" do
|
412
391
|
story = Story.create(:text => 'hello')
|
413
392
|
job = story.delay.tell
|
414
393
|
story.update_attributes :text => 'goodbye'
|
415
|
-
job.reload.payload_object.object.text.
|
394
|
+
expect(job.reload.payload_object.object.text).to eq('goodbye')
|
416
395
|
end
|
417
396
|
|
418
|
-
it "
|
397
|
+
it "raises error ArgumentError the record is not persisted" do
|
419
398
|
story = Story.new(:text => 'hello')
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
399
|
+
if story.respond_to?(:new_record?)
|
400
|
+
expect {
|
401
|
+
story.delay.tell
|
402
|
+
}.to raise_error(ArgumentError, "Jobs cannot be created for records before they've been persisted")
|
403
|
+
end
|
424
404
|
end
|
425
405
|
|
426
|
-
it "
|
406
|
+
it "raises deserialization error for destroyed records" do
|
427
407
|
story = Story.create(:text => 'hello')
|
428
408
|
job = story.delay.tell
|
429
409
|
story.destroy
|
430
|
-
|
410
|
+
expect {
|
431
411
|
job.reload.payload_object
|
432
|
-
}.
|
412
|
+
}.to raise_error(Delayed::DeserializationError)
|
433
413
|
end
|
434
414
|
end
|
435
415
|
|
@@ -440,26 +420,26 @@ shared_examples_for 'a delayed_job backend' do
|
|
440
420
|
end
|
441
421
|
|
442
422
|
describe "running a job" do
|
443
|
-
it "
|
423
|
+
it "fails after Worker.max_run_time" do
|
444
424
|
begin
|
445
425
|
old_max_run_time = Delayed::Worker.max_run_time
|
446
426
|
Delayed::Worker.max_run_time = 1.second
|
447
427
|
job = Delayed::Job.create :payload_object => LongRunningJob.new
|
448
428
|
worker.run(job)
|
449
|
-
job.reload.last_error.
|
450
|
-
job.attempts.
|
429
|
+
expect(job.reload.last_error).to match(/expired/)
|
430
|
+
expect(job.attempts).to eq(1)
|
451
431
|
ensure
|
452
432
|
Delayed::Worker.max_run_time = old_max_run_time
|
453
433
|
end
|
454
434
|
end
|
455
435
|
|
456
436
|
context "when the job raises a deserialization error" do
|
457
|
-
it "
|
437
|
+
it "marks the job as failed" do
|
458
438
|
Delayed::Worker.destroy_failed_jobs = false
|
459
439
|
job = described_class.create! :handler => "--- !ruby/object:JobThatDoesNotExist {}"
|
460
440
|
worker.work_off
|
461
441
|
job.reload
|
462
|
-
job.
|
442
|
+
expect(job).to be_failed
|
463
443
|
end
|
464
444
|
end
|
465
445
|
end
|
@@ -473,41 +453,41 @@ shared_examples_for 'a delayed_job backend' do
|
|
473
453
|
@job = Delayed::Job.enqueue(ErrorJob.new)
|
474
454
|
end
|
475
455
|
|
476
|
-
it "
|
456
|
+
it "records last_error when destroy_failed_jobs = false, max_attempts = 1" do
|
477
457
|
Delayed::Worker.destroy_failed_jobs = false
|
478
458
|
Delayed::Worker.max_attempts = 1
|
479
459
|
worker.run(@job)
|
480
460
|
@job.reload
|
481
|
-
@job.last_error.
|
482
|
-
@job.attempts.
|
483
|
-
@job.
|
461
|
+
expect(@job.last_error).to match(/did not work/)
|
462
|
+
expect(@job.attempts).to eq(1)
|
463
|
+
expect(@job).to be_failed
|
484
464
|
end
|
485
465
|
|
486
|
-
it "
|
466
|
+
it "re-schedules jobs after failing" do
|
487
467
|
worker.work_off
|
488
468
|
@job.reload
|
489
|
-
@job.last_error.
|
490
|
-
@job.last_error.
|
491
|
-
@job.attempts.
|
492
|
-
@job.run_at.
|
493
|
-
@job.run_at.
|
494
|
-
@job.locked_by.
|
495
|
-
@job.locked_at.
|
469
|
+
expect(@job.last_error).to match(/did not work/)
|
470
|
+
expect(@job.last_error).to match(/sample_jobs.rb:\d+:in `perform'/)
|
471
|
+
expect(@job.attempts).to eq(1)
|
472
|
+
expect(@job.run_at).to be > Delayed::Job.db_time_now - 10.minutes
|
473
|
+
expect(@job.run_at).to be < Delayed::Job.db_time_now + 10.minutes
|
474
|
+
expect(@job.locked_by).to be_nil
|
475
|
+
expect(@job.locked_at).to be_nil
|
496
476
|
end
|
497
477
|
|
498
|
-
it
|
478
|
+
it "re-schedules jobs with handler provided time if present" do
|
499
479
|
job = Delayed::Job.enqueue(CustomRescheduleJob.new(99.minutes))
|
500
480
|
worker.run(job)
|
501
481
|
job.reload
|
502
482
|
|
503
|
-
(Delayed::Job.db_time_now + 99.minutes - job.run_at).abs.
|
483
|
+
expect((Delayed::Job.db_time_now + 99.minutes - job.run_at).abs).to be < 1
|
504
484
|
end
|
505
485
|
|
506
|
-
it "
|
486
|
+
it "does not fail when the triggered error doesn't have a message" do
|
507
487
|
error_with_nil_message = StandardError.new
|
508
488
|
error_with_nil_message.stub!(:message).and_return nil
|
509
489
|
@job.stub!(:invoke_job).and_raise error_with_nil_message
|
510
|
-
|
490
|
+
expect{worker.run(@job)}.not_to raise_error
|
511
491
|
end
|
512
492
|
end
|
513
493
|
|
@@ -520,10 +500,10 @@ shared_examples_for 'a delayed_job backend' do
|
|
520
500
|
context "when the job's payload has a #failure hook" do
|
521
501
|
before do
|
522
502
|
@job = Delayed::Job.create :payload_object => OnPermanentFailureJob.new
|
523
|
-
@job.payload_object.
|
503
|
+
expect(@job.payload_object).to respond_to :failure
|
524
504
|
end
|
525
505
|
|
526
|
-
it "
|
506
|
+
it "runs that hook" do
|
527
507
|
@job.payload_object.should_receive :failure
|
528
508
|
worker.reschedule(@job)
|
529
509
|
end
|
@@ -543,13 +523,13 @@ shared_examples_for 'a delayed_job backend' do
|
|
543
523
|
# failure method).
|
544
524
|
|
545
525
|
before do
|
546
|
-
@job.payload_object.
|
526
|
+
expect(@job.payload_object).not_to respond_to(:failure)
|
547
527
|
end
|
548
528
|
|
549
|
-
it "
|
550
|
-
|
529
|
+
it "does not try to run that hook" do
|
530
|
+
expect {
|
551
531
|
Delayed::Worker.max_attempts.times { worker.reschedule(@job) }
|
552
|
-
|
532
|
+
}.not_to raise_exception(NoMethodError)
|
553
533
|
end
|
554
534
|
end
|
555
535
|
end
|
@@ -561,12 +541,12 @@ shared_examples_for 'a delayed_job backend' do
|
|
561
541
|
|
562
542
|
it_should_behave_like "any failure more than Worker.max_attempts times"
|
563
543
|
|
564
|
-
it "
|
544
|
+
it "is destroyed if it failed more than Worker.max_attempts times" do
|
565
545
|
@job.should_receive(:destroy)
|
566
546
|
Delayed::Worker.max_attempts.times { worker.reschedule(@job) }
|
567
547
|
end
|
568
548
|
|
569
|
-
it "
|
549
|
+
it "is not destroyed if failed fewer than Worker.max_attempts times" do
|
570
550
|
@job.should_not_receive(:destroy)
|
571
551
|
(Delayed::Worker.max_attempts - 1).times { worker.reschedule(@job) }
|
572
552
|
end
|
@@ -579,15 +559,15 @@ shared_examples_for 'a delayed_job backend' do
|
|
579
559
|
|
580
560
|
it_should_behave_like "any failure more than Worker.max_attempts times"
|
581
561
|
|
582
|
-
it "
|
583
|
-
@job.reload.
|
562
|
+
it "is failed if it failed more than Worker.max_attempts times" do
|
563
|
+
expect(@job.reload).not_to be_failed
|
584
564
|
Delayed::Worker.max_attempts.times { worker.reschedule(@job) }
|
585
|
-
@job.reload.
|
565
|
+
expect(@job.reload).to be_failed
|
586
566
|
end
|
587
567
|
|
588
|
-
it "
|
568
|
+
it "is not failed if it failed fewer than Worker.max_attempts times" do
|
589
569
|
(Delayed::Worker.max_attempts - 1).times { worker.reschedule(@job) }
|
590
|
-
@job.reload.
|
570
|
+
expect(@job.reload).not_to be_failed
|
591
571
|
end
|
592
572
|
end
|
593
573
|
end
|