delayed_job 2.1.1 → 2.1.2
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 +17 -3
- data/lib/delayed/backend/active_record.rb +14 -22
- data/lib/delayed/backend/base.rb +13 -9
- data/lib/delayed/backend/shared_spec.rb +65 -67
- data/lib/delayed/performable_mailer.rb +6 -6
- data/lib/delayed/railtie.rb +4 -0
- data/lib/delayed/recipes.rb +18 -3
- data/lib/delayed/tasks.rb +1 -1
- data/lib/delayed/worker.rb +5 -1
- data/lib/generators/delayed_job/templates/migration.rb +1 -0
- data/spec/sample_jobs.rb +2 -2
- data/spec/spec_helper.rb +4 -1
- metadata +24 -17
data/README.textile
CHANGED
@@ -24,7 +24,7 @@ gem 'delayed_job'
|
|
24
24
|
|
25
25
|
After delayed_job is installed, you will need to setup the backend.
|
26
26
|
|
27
|
-
|
27
|
+
h3. Backends
|
28
28
|
|
29
29
|
delayed_job supports multiple backends for storing the job queue. "See the wiki for other backends":http://wiki.github.com/collectiveidea/delayed_job/backends besides Active Record.
|
30
30
|
|
@@ -41,10 +41,10 @@ Call @.delay.method(params)@ on any object and it will be processed in the backg
|
|
41
41
|
|
42
42
|
<pre>
|
43
43
|
# without delayed_job
|
44
|
-
|
44
|
+
@user.activate!(@device)
|
45
45
|
|
46
46
|
# with delayed_job
|
47
|
-
|
47
|
+
@user.delay.activate!(@device)
|
48
48
|
</pre>
|
49
49
|
|
50
50
|
If a method should always be run in the background, you can call @#handle_asynchronously@ after the method declaration:
|
@@ -94,6 +94,20 @@ class LongTasks
|
|
94
94
|
end
|
95
95
|
</pre>
|
96
96
|
|
97
|
+
h3. Rails 3 Mailers
|
98
|
+
|
99
|
+
Due to how mailers are implemented in Rails 3, we had to do a little work around to get delayed_job to work.
|
100
|
+
|
101
|
+
<pre>
|
102
|
+
# without delayed_job
|
103
|
+
Notifier.signup(@user).deliver
|
104
|
+
|
105
|
+
# with delayed_job
|
106
|
+
Notifier.delay.signup(@user)
|
107
|
+
</pre>
|
108
|
+
|
109
|
+
Remove the @.deliver@ method to make it work. It's not ideal, but it's the best we could do for now.
|
110
|
+
|
97
111
|
h2. Running Jobs
|
98
112
|
|
99
113
|
@script/delayed_job@ can be used to manage a background process which will start working off jobs. Make sure you've run `script/generate delayed_job`.
|
@@ -16,6 +16,10 @@ module Delayed
|
|
16
16
|
}
|
17
17
|
scope :by_priority, order('priority ASC, run_at ASC')
|
18
18
|
|
19
|
+
scope :locked_by_worker, lambda{|worker_name, max_run_time|
|
20
|
+
where(['locked_by = ? AND locked_at > ?', worker_name, db_time_now - max_run_time])
|
21
|
+
}
|
22
|
+
|
19
23
|
def self.before_fork
|
20
24
|
::ActiveRecord::Base.clear_all_connections!
|
21
25
|
end
|
@@ -29,37 +33,25 @@ module Delayed
|
|
29
33
|
update_all("locked_by = null, locked_at = null", ["locked_by = ?", worker_name])
|
30
34
|
end
|
31
35
|
|
32
|
-
|
33
|
-
def self.find_available(worker_name, limit = 5, max_run_time = Worker.max_run_time)
|
36
|
+
def self.jobs_available_to_worker(worker_name, max_run_time)
|
34
37
|
scope = self.ready_to_run(worker_name, max_run_time)
|
35
38
|
scope = scope.scoped(:conditions => ['priority >= ?', Worker.min_priority]) if Worker.min_priority
|
36
39
|
scope = scope.scoped(:conditions => ['priority <= ?', Worker.max_priority]) if Worker.max_priority
|
40
|
+
scope.by_priority
|
41
|
+
end
|
37
42
|
|
43
|
+
# Reserve a single job in a single update query. This causes workers to serialize on the
|
44
|
+
# database and avoids contention.
|
45
|
+
def self.reserve(worker, max_run_time = Worker.max_run_time)
|
46
|
+
affected_rows = 0
|
38
47
|
::ActiveRecord::Base.silence do
|
39
|
-
|
48
|
+
affected_rows = jobs_available_to_worker(worker.name, max_run_time).limit(1).update_all(["locked_at = ?, locked_by = ?", db_time_now, worker.name])
|
40
49
|
end
|
41
|
-
end
|
42
50
|
|
43
|
-
# Lock this job for this worker.
|
44
|
-
# Returns true if we have the lock, false otherwise.
|
45
|
-
def lock_exclusively!(max_run_time, worker)
|
46
|
-
now = self.class.db_time_now
|
47
|
-
affected_rows = if locked_by != worker
|
48
|
-
# We don't own this job so we will update the locked_by name and the locked_at
|
49
|
-
self.class.update_all(["locked_at = ?, locked_by = ?", now, worker], ["id = ? and (locked_at is null or locked_at < ?) and (run_at <= ?)", id, (now - max_run_time.to_i), now])
|
50
|
-
else
|
51
|
-
# We already own this job, this may happen if the job queue crashes.
|
52
|
-
# Simply resume and update the locked_at
|
53
|
-
self.class.update_all(["locked_at = ?", now], ["id = ? and locked_by = ?", id, worker])
|
54
|
-
end
|
55
51
|
if affected_rows == 1
|
56
|
-
|
57
|
-
self.locked_by = worker
|
58
|
-
self.locked_at_will_change!
|
59
|
-
self.locked_by_will_change!
|
60
|
-
return true
|
52
|
+
locked_by_worker(worker.name, max_run_time).first
|
61
53
|
else
|
62
|
-
|
54
|
+
nil
|
63
55
|
end
|
64
56
|
end
|
65
57
|
|
data/lib/delayed/backend/base.rb
CHANGED
@@ -10,14 +10,14 @@ module Delayed
|
|
10
10
|
def enqueue(*args)
|
11
11
|
options = {
|
12
12
|
:priority => Delayed::Worker.default_priority
|
13
|
-
}
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
options[:priority]
|
20
|
-
options[:run_at]
|
13
|
+
}.merge!(args.extract_options!)
|
14
|
+
|
15
|
+
options[:payload_object] ||= args.shift
|
16
|
+
|
17
|
+
if args.size > 0
|
18
|
+
warn "[DEPRECATION] Passing multiple arguments to `#enqueue` is deprecated. Pass a hash with :priority and :run_at."
|
19
|
+
options[:priority] = args.first || options[:priority]
|
20
|
+
options[:run_at] = args[1]
|
21
21
|
end
|
22
22
|
|
23
23
|
unless options[:payload_object].respond_to?(:perform)
|
@@ -109,7 +109,11 @@ module Delayed
|
|
109
109
|
payload_object.reschedule_at(self.class.db_time_now, attempts) :
|
110
110
|
self.class.db_time_now + (attempts ** 4) + 5
|
111
111
|
end
|
112
|
-
|
112
|
+
|
113
|
+
def max_attempts
|
114
|
+
payload_object.max_attempts if payload_object.respond_to?(:max_attempts)
|
115
|
+
end
|
116
|
+
|
113
117
|
protected
|
114
118
|
|
115
119
|
def set_default_run_at
|
@@ -1,6 +1,8 @@
|
|
1
1
|
require File.expand_path('../../../../spec/sample_jobs', __FILE__)
|
2
2
|
|
3
3
|
shared_examples_for 'a delayed_job backend' do
|
4
|
+
let(:worker) { Delayed::Worker.new }
|
5
|
+
|
4
6
|
def create_job(opts = {})
|
5
7
|
described_class.create(opts.merge(:payload_object => SimpleJob.new))
|
6
8
|
end
|
@@ -56,9 +58,11 @@ shared_examples_for 'a delayed_job backend' do
|
|
56
58
|
described_class.count.should == 1
|
57
59
|
end
|
58
60
|
|
59
|
-
it "should be able to set priority" do
|
60
|
-
|
61
|
-
|
61
|
+
it "should be able to set priority [DEPRECATED]" do
|
62
|
+
silence_warnings do
|
63
|
+
job = described_class.enqueue SimpleJob.new, 5
|
64
|
+
job.priority.should == 5
|
65
|
+
end
|
62
66
|
end
|
63
67
|
|
64
68
|
it "should use default priority when it is not set" do
|
@@ -66,10 +70,12 @@ shared_examples_for 'a delayed_job backend' do
|
|
66
70
|
@job.priority.should == 99
|
67
71
|
end
|
68
72
|
|
69
|
-
it "should be able to set run_at" do
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
+
it "should be able to set run_at [DEPRECATED]" do
|
74
|
+
silence_warnings do
|
75
|
+
later = described_class.db_time_now + 5.minutes
|
76
|
+
@job = described_class.enqueue SimpleJob.new, 5, later
|
77
|
+
@job.run_at.should be_within(1).of(later)
|
78
|
+
end
|
73
79
|
end
|
74
80
|
|
75
81
|
it "should work with jobs in modules" do
|
@@ -137,58 +143,39 @@ shared_examples_for 'a delayed_job backend' do
|
|
137
143
|
describe "reserve" do
|
138
144
|
before do
|
139
145
|
Delayed::Worker.max_run_time = 2.minutes
|
140
|
-
@worker = Delayed::Worker.new(:quiet => true)
|
141
146
|
end
|
142
147
|
|
143
148
|
it "should not reserve failed jobs" do
|
144
149
|
create_job :attempts => 50, :failed_at => described_class.db_time_now
|
145
|
-
described_class.reserve(
|
150
|
+
described_class.reserve(worker).should be_nil
|
146
151
|
end
|
147
152
|
|
148
153
|
it "should not reserve jobs scheduled for the future" do
|
149
|
-
create_job :run_at =>
|
150
|
-
described_class.reserve(
|
154
|
+
create_job :run_at => described_class.db_time_now + 1.minute
|
155
|
+
described_class.reserve(worker).should be_nil
|
151
156
|
end
|
152
157
|
|
153
|
-
it "should
|
158
|
+
it "should not reserve jobs locked by other workers" do
|
154
159
|
job = create_job
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
described_class.reserve(
|
160
|
+
other_worker = Delayed::Worker.new
|
161
|
+
other_worker.name = 'other_worker'
|
162
|
+
described_class.reserve(other_worker).should == job
|
163
|
+
described_class.reserve(worker).should be_nil
|
159
164
|
end
|
160
165
|
|
161
166
|
it "should reserve open jobs" do
|
162
167
|
job = create_job
|
163
|
-
described_class.reserve(
|
168
|
+
described_class.reserve(worker).should == job
|
164
169
|
end
|
165
170
|
|
166
171
|
it "should reserve expired jobs" do
|
167
|
-
job = create_job(:locked_by =>
|
168
|
-
described_class.reserve(
|
172
|
+
job = create_job(:locked_by => worker.name, :locked_at => described_class.db_time_now - 3.minutes)
|
173
|
+
described_class.reserve(worker).should == job
|
169
174
|
end
|
170
175
|
|
171
176
|
it "should reserve own jobs" do
|
172
|
-
job = create_job(:locked_by =>
|
173
|
-
described_class.reserve(
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
context "when another worker has worked on a task since the job was found to be available, it" do
|
178
|
-
|
179
|
-
before :each do
|
180
|
-
@job = described_class.create :payload_object => SimpleJob.new
|
181
|
-
@job_copy_for_worker_2 = described_class.find(@job.id)
|
182
|
-
end
|
183
|
-
|
184
|
-
it "should not allow a second worker to get exclusive access if already successfully processed by worker1" do
|
185
|
-
@job.destroy
|
186
|
-
@job_copy_for_worker_2.lock_exclusively!(4.hours, 'worker2').should == false
|
187
|
-
end
|
188
|
-
|
189
|
-
it "should not allow a second worker to get exclusive access if failed to be processed by worker1 and run_at time is now in future (due to backing off behaviour)" do
|
190
|
-
@job.update_attributes(:attempts => 1, :run_at => described_class.db_time_now + 1.day)
|
191
|
-
@job_copy_for_worker_2.lock_exclusively!(4.hours, 'worker2').should == false
|
177
|
+
job = create_job(:locked_by => worker.name, :locked_at => (described_class.db_time_now - 1.minutes))
|
178
|
+
described_class.reserve(worker).should == job
|
192
179
|
end
|
193
180
|
end
|
194
181
|
|
@@ -222,8 +209,9 @@ shared_examples_for 'a delayed_job backend' do
|
|
222
209
|
end
|
223
210
|
|
224
211
|
it "should fetch jobs ordered by priority" do
|
225
|
-
10.times { described_class.enqueue SimpleJob.new, rand(10) }
|
226
|
-
jobs =
|
212
|
+
10.times { described_class.enqueue SimpleJob.new, :priority => rand(10) }
|
213
|
+
jobs = []
|
214
|
+
10.times { jobs << described_class.reserve(worker) }
|
227
215
|
jobs.size.should == 10
|
228
216
|
jobs.each_cons(2) do |a, b|
|
229
217
|
a.priority.should <= b.priority
|
@@ -233,33 +221,31 @@ shared_examples_for 'a delayed_job backend' do
|
|
233
221
|
it "should only find jobs greater than or equal to min priority" do
|
234
222
|
min = 5
|
235
223
|
Delayed::Worker.min_priority = min
|
236
|
-
10.times {|i| described_class.enqueue SimpleJob.new, i }
|
237
|
-
|
238
|
-
jobs.each {|job| job.priority.should >= min}
|
224
|
+
10.times {|i| described_class.enqueue SimpleJob.new, :priority => i }
|
225
|
+
5.times { described_class.reserve(worker).priority.should >= min }
|
239
226
|
end
|
240
227
|
|
241
228
|
it "should only find jobs less than or equal to max priority" do
|
242
229
|
max = 5
|
243
230
|
Delayed::Worker.max_priority = max
|
244
|
-
10.times {|i| described_class.enqueue SimpleJob.new, i }
|
245
|
-
|
246
|
-
jobs.each {|job| job.priority.should <= max}
|
231
|
+
10.times {|i| described_class.enqueue SimpleJob.new, :priority => i }
|
232
|
+
5.times { described_class.reserve(worker).priority.should <= max }
|
247
233
|
end
|
248
234
|
end
|
249
235
|
|
250
236
|
context "clear_locks!" do
|
251
237
|
before do
|
252
|
-
@job = create_job(:locked_by => '
|
238
|
+
@job = create_job(:locked_by => 'worker1', :locked_at => described_class.db_time_now)
|
253
239
|
end
|
254
240
|
|
255
241
|
it "should clear locks for the given worker" do
|
256
|
-
described_class.clear_locks!('
|
257
|
-
described_class.
|
242
|
+
described_class.clear_locks!('worker1')
|
243
|
+
described_class.reserve(worker).should == @job
|
258
244
|
end
|
259
245
|
|
260
246
|
it "should not clear locks for other workers" do
|
261
|
-
described_class.clear_locks!('
|
262
|
-
described_class.
|
247
|
+
described_class.clear_locks!('different_worker')
|
248
|
+
described_class.reserve(worker).should_not == @job
|
263
249
|
end
|
264
250
|
end
|
265
251
|
|
@@ -285,6 +271,21 @@ shared_examples_for 'a delayed_job backend' do
|
|
285
271
|
@job.id.should_not be_nil
|
286
272
|
end
|
287
273
|
end
|
274
|
+
|
275
|
+
context "max_attempts" do
|
276
|
+
before(:each) do
|
277
|
+
@job = described_class.enqueue SimpleJob.new
|
278
|
+
end
|
279
|
+
|
280
|
+
it 'should not be defined' do
|
281
|
+
@job.max_attempts.should be_nil
|
282
|
+
end
|
283
|
+
|
284
|
+
it 'should use the max_retries value on the payload when defined' do
|
285
|
+
@job.payload_object.stub!(:max_attempts).and_return(99)
|
286
|
+
@job.max_attempts.should == 99
|
287
|
+
end
|
288
|
+
end
|
288
289
|
|
289
290
|
describe "yaml serialization" do
|
290
291
|
it "should reload changed attributes" do
|
@@ -307,9 +308,6 @@ shared_examples_for 'a delayed_job backend' do
|
|
307
308
|
describe "worker integration" do
|
308
309
|
before do
|
309
310
|
Delayed::Job.delete_all
|
310
|
-
|
311
|
-
@worker = Delayed::Worker.new(:max_priority => nil, :min_priority => nil, :quiet => true)
|
312
|
-
|
313
311
|
SimpleJob.runs = 0
|
314
312
|
end
|
315
313
|
|
@@ -319,7 +317,7 @@ shared_examples_for 'a delayed_job backend' do
|
|
319
317
|
old_max_run_time = Delayed::Worker.max_run_time
|
320
318
|
Delayed::Worker.max_run_time = 1.second
|
321
319
|
@job = Delayed::Job.create :payload_object => LongRunningJob.new
|
322
|
-
|
320
|
+
worker.run(@job)
|
323
321
|
@job.reload.last_error.should =~ /expired/
|
324
322
|
@job.attempts.should == 1
|
325
323
|
ensure
|
@@ -331,7 +329,7 @@ shared_examples_for 'a delayed_job backend' do
|
|
331
329
|
it "should mark the job as failed" do
|
332
330
|
Delayed::Worker.destroy_failed_jobs = false
|
333
331
|
job = described_class.create! :handler => "--- !ruby/object:JobThatDoesNotExist {}"
|
334
|
-
|
332
|
+
worker.work_off
|
335
333
|
job.reload
|
336
334
|
job.failed_at.should_not be_nil
|
337
335
|
end
|
@@ -350,7 +348,7 @@ shared_examples_for 'a delayed_job backend' do
|
|
350
348
|
it "should record last_error when destroy_failed_jobs = false, max_attempts = 1" do
|
351
349
|
Delayed::Worker.destroy_failed_jobs = false
|
352
350
|
Delayed::Worker.max_attempts = 1
|
353
|
-
|
351
|
+
worker.run(@job)
|
354
352
|
@job.reload
|
355
353
|
@job.last_error.should =~ /did not work/
|
356
354
|
@job.attempts.should == 1
|
@@ -358,7 +356,7 @@ shared_examples_for 'a delayed_job backend' do
|
|
358
356
|
end
|
359
357
|
|
360
358
|
it "should re-schedule jobs after failing" do
|
361
|
-
|
359
|
+
worker.work_off
|
362
360
|
@job.reload
|
363
361
|
@job.last_error.should =~ /did not work/
|
364
362
|
@job.last_error.should =~ /sample_jobs.rb:\d+:in `perform'/
|
@@ -371,7 +369,7 @@ shared_examples_for 'a delayed_job backend' do
|
|
371
369
|
|
372
370
|
it 'should re-schedule with handler provided time if present' do
|
373
371
|
@job = Delayed::Job.enqueue(CustomRescheduleJob.new(99.minutes))
|
374
|
-
|
372
|
+
worker.run(@job)
|
375
373
|
@job.reload
|
376
374
|
|
377
375
|
(Delayed::Job.db_time_now + 99.minutes - @job.run_at).abs.should < 1
|
@@ -381,7 +379,7 @@ shared_examples_for 'a delayed_job backend' do
|
|
381
379
|
error_with_nil_message = StandardError.new
|
382
380
|
error_with_nil_message.stub!(:message).and_return nil
|
383
381
|
@job.stub!(:invoke_job).and_raise error_with_nil_message
|
384
|
-
lambda{
|
382
|
+
lambda{worker.run(@job)}.should_not raise_error
|
385
383
|
end
|
386
384
|
end
|
387
385
|
|
@@ -399,7 +397,7 @@ shared_examples_for 'a delayed_job backend' do
|
|
399
397
|
|
400
398
|
it "should run that hook" do
|
401
399
|
@job.payload_object.should_receive :failure
|
402
|
-
|
400
|
+
worker.reschedule(@job)
|
403
401
|
end
|
404
402
|
end
|
405
403
|
|
@@ -422,7 +420,7 @@ shared_examples_for 'a delayed_job backend' do
|
|
422
420
|
|
423
421
|
it "should not try to run that hook" do
|
424
422
|
lambda do
|
425
|
-
Delayed::Worker.max_attempts.times {
|
423
|
+
Delayed::Worker.max_attempts.times { worker.reschedule(@job) }
|
426
424
|
end.should_not raise_exception(NoMethodError)
|
427
425
|
end
|
428
426
|
end
|
@@ -437,12 +435,12 @@ shared_examples_for 'a delayed_job backend' do
|
|
437
435
|
|
438
436
|
it "should be destroyed if it failed more than Worker.max_attempts times" do
|
439
437
|
@job.should_receive(:destroy)
|
440
|
-
Delayed::Worker.max_attempts.times {
|
438
|
+
Delayed::Worker.max_attempts.times { worker.reschedule(@job) }
|
441
439
|
end
|
442
440
|
|
443
441
|
it "should not be destroyed if failed fewer than Worker.max_attempts times" do
|
444
442
|
@job.should_not_receive(:destroy)
|
445
|
-
(Delayed::Worker.max_attempts - 1).times {
|
443
|
+
(Delayed::Worker.max_attempts - 1).times { worker.reschedule(@job) }
|
446
444
|
end
|
447
445
|
end
|
448
446
|
|
@@ -455,12 +453,12 @@ shared_examples_for 'a delayed_job backend' do
|
|
455
453
|
|
456
454
|
it "should be failed if it failed more than Worker.max_attempts times" do
|
457
455
|
@job.reload.failed_at.should == nil
|
458
|
-
Delayed::Worker.max_attempts.times {
|
456
|
+
Delayed::Worker.max_attempts.times { worker.reschedule(@job) }
|
459
457
|
@job.reload.failed_at.should_not == nil
|
460
458
|
end
|
461
459
|
|
462
460
|
it "should not be failed if it failed fewer than Worker.max_attempts times" do
|
463
|
-
(Delayed::Worker.max_attempts - 1).times {
|
461
|
+
(Delayed::Worker.max_attempts - 1).times { worker.reschedule(@job) }
|
464
462
|
@job.reload.failed_at.should == nil
|
465
463
|
end
|
466
464
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'mail'
|
2
2
|
|
3
3
|
module Delayed
|
4
4
|
class PerformableMailer < PerformableMethod
|
@@ -6,11 +6,11 @@ module Delayed
|
|
6
6
|
object.send(method_name, *args).deliver
|
7
7
|
end
|
8
8
|
end
|
9
|
-
end
|
10
9
|
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
module DelayMail
|
11
|
+
def delay(options = {})
|
12
|
+
DelayProxy.new(PerformableMailer, self, options)
|
13
|
+
end
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
@@ -18,4 +18,4 @@ Mail::Message.class_eval do
|
|
18
18
|
def delay(*args)
|
19
19
|
raise RuntimeError, "Use MyMailer.delay.mailer_action(args) to delay sending of emails."
|
20
20
|
end
|
21
|
-
end
|
21
|
+
end
|
data/lib/delayed/railtie.rb
CHANGED
data/lib/delayed/recipes.rb
CHANGED
@@ -6,6 +6,17 @@
|
|
6
6
|
# after "deploy:stop", "delayed_job:stop"
|
7
7
|
# after "deploy:start", "delayed_job:start"
|
8
8
|
# after "deploy:restart", "delayed_job:restart"
|
9
|
+
#
|
10
|
+
# If you want to use command line options, for example to start multiple workers,
|
11
|
+
# define a Capistrano variable delayed_job_args:
|
12
|
+
#
|
13
|
+
# set :delayed_jobs_args, "-n 2"
|
14
|
+
#
|
15
|
+
# If you've got delayed_job workers running on a servers, you can also specify
|
16
|
+
# which servers have delayed_job running and should be restarted after deploy.
|
17
|
+
#
|
18
|
+
# set :delayed_job_server_role, :worker
|
19
|
+
#
|
9
20
|
|
10
21
|
Capistrano::Configuration.instance.load do
|
11
22
|
namespace :delayed_job do
|
@@ -17,18 +28,22 @@ Capistrano::Configuration.instance.load do
|
|
17
28
|
fetch(:delayed_job_args, "")
|
18
29
|
end
|
19
30
|
|
31
|
+
def roles
|
32
|
+
fetch(:delayed_job_server_role, :app)
|
33
|
+
end
|
34
|
+
|
20
35
|
desc "Stop the delayed_job process"
|
21
|
-
task :stop, :roles =>
|
36
|
+
task :stop, :roles => lambda { roles } do
|
22
37
|
run "cd #{current_path};#{rails_env} script/delayed_job stop"
|
23
38
|
end
|
24
39
|
|
25
40
|
desc "Start the delayed_job process"
|
26
|
-
task :start, :roles =>
|
41
|
+
task :start, :roles => lambda { roles } do
|
27
42
|
run "cd #{current_path};#{rails_env} script/delayed_job start #{args}"
|
28
43
|
end
|
29
44
|
|
30
45
|
desc "Restart the delayed_job process"
|
31
|
-
task :restart, :roles =>
|
46
|
+
task :restart, :roles => lambda { roles } do
|
32
47
|
run "cd #{current_path};#{rails_env} script/delayed_job restart #{args}"
|
33
48
|
end
|
34
49
|
end
|
data/lib/delayed/tasks.rb
CHANGED
@@ -6,6 +6,6 @@ namespace :jobs do
|
|
6
6
|
|
7
7
|
desc "Start a delayed_job worker."
|
8
8
|
task :work => :environment do
|
9
|
-
Delayed::Worker.new(:min_priority => ENV['MIN_PRIORITY'], :max_priority => ENV['MAX_PRIORITY']).start
|
9
|
+
Delayed::Worker.new(:min_priority => ENV['MIN_PRIORITY'], :max_priority => ENV['MAX_PRIORITY'], :quiet => false).start
|
10
10
|
end
|
11
11
|
end
|
data/lib/delayed/worker.rb
CHANGED
@@ -132,7 +132,7 @@ module Delayed
|
|
132
132
|
# Reschedule the job in the future (when a job fails).
|
133
133
|
# Uses an exponential scale depending on the number of failed attempts.
|
134
134
|
def reschedule(job, time = nil)
|
135
|
-
if (job.attempts += 1) <
|
135
|
+
if (job.attempts += 1) < max_attempts(job)
|
136
136
|
time ||= job.reschedule_at
|
137
137
|
job.run_at = time
|
138
138
|
job.unlock
|
@@ -157,6 +157,10 @@ module Delayed
|
|
157
157
|
logger.add level, "#{Time.now.strftime('%FT%T%z')}: #{text}" if logger
|
158
158
|
end
|
159
159
|
|
160
|
+
def max_attempts(job)
|
161
|
+
job.max_attempts || self.class.max_attempts
|
162
|
+
end
|
163
|
+
|
160
164
|
protected
|
161
165
|
|
162
166
|
def handle_failed_job(job, error)
|
data/spec/sample_jobs.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -5,6 +5,7 @@ require 'bundler/setup'
|
|
5
5
|
require 'rspec'
|
6
6
|
require 'logger'
|
7
7
|
|
8
|
+
require 'rails'
|
8
9
|
require 'active_record'
|
9
10
|
require 'action_mailer'
|
10
11
|
|
@@ -13,7 +14,6 @@ require 'delayed/backend/shared_spec'
|
|
13
14
|
|
14
15
|
Delayed::Worker.logger = Logger.new('/tmp/dj.log')
|
15
16
|
ENV['RAILS_ENV'] = 'test'
|
16
|
-
require 'rails'
|
17
17
|
|
18
18
|
config = YAML.load(File.read('spec/database.yml'))
|
19
19
|
ActiveRecord::Base.configurations = {'test' => config['mysql']}
|
@@ -53,3 +53,6 @@ Delayed::Worker.backend = :active_record
|
|
53
53
|
|
54
54
|
# Add this directory so the ActiveSupport autoloading works
|
55
55
|
ActiveSupport::Dependencies.autoload_paths << File.dirname(__FILE__)
|
56
|
+
|
57
|
+
# Add this to simulate Railtie initializer being executed
|
58
|
+
ActionMailer::Base.send(:extend, Delayed::DelayMail)
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: delayed_job
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 15
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 2
|
7
8
|
- 1
|
8
|
-
-
|
9
|
-
version: 2.1.
|
9
|
+
- 2
|
10
|
+
version: 2.1.2
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Brandon Keepers
|
@@ -15,16 +16,18 @@ autorequire:
|
|
15
16
|
bindir: bin
|
16
17
|
cert_chain: []
|
17
18
|
|
18
|
-
date: 2010-
|
19
|
+
date: 2010-12-01 00:00:00 -05:00
|
19
20
|
default_executable:
|
20
21
|
dependencies:
|
21
22
|
- !ruby/object:Gem::Dependency
|
22
23
|
name: daemons
|
23
24
|
prerelease: false
|
24
25
|
requirement: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
25
27
|
requirements:
|
26
28
|
- - ">="
|
27
29
|
- !ruby/object:Gem::Version
|
30
|
+
hash: 3
|
28
31
|
segments:
|
29
32
|
- 0
|
30
33
|
version: "0"
|
@@ -34,9 +37,11 @@ dependencies:
|
|
34
37
|
name: activesupport
|
35
38
|
prerelease: false
|
36
39
|
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
37
41
|
requirements:
|
38
42
|
- - ~>
|
39
43
|
- !ruby/object:Gem::Version
|
44
|
+
hash: 7
|
40
45
|
segments:
|
41
46
|
- 3
|
42
47
|
- 0
|
@@ -47,9 +52,11 @@ dependencies:
|
|
47
52
|
name: rspec
|
48
53
|
prerelease: false
|
49
54
|
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
50
56
|
requirements:
|
51
57
|
- - ~>
|
52
58
|
- !ruby/object:Gem::Version
|
59
|
+
hash: 3
|
53
60
|
segments:
|
54
61
|
- 2
|
55
62
|
- 0
|
@@ -60,9 +67,11 @@ dependencies:
|
|
60
67
|
name: rake
|
61
68
|
prerelease: false
|
62
69
|
requirement: &id004 !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
63
71
|
requirements:
|
64
72
|
- - ">="
|
65
73
|
- !ruby/object:Gem::Version
|
74
|
+
hash: 3
|
66
75
|
segments:
|
67
76
|
- 0
|
68
77
|
version: "0"
|
@@ -72,9 +81,11 @@ dependencies:
|
|
72
81
|
name: rails
|
73
82
|
prerelease: false
|
74
83
|
requirement: &id005 !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
75
85
|
requirements:
|
76
86
|
- - ~>
|
77
87
|
- !ruby/object:Gem::Version
|
88
|
+
hash: 7
|
78
89
|
segments:
|
79
90
|
- 3
|
80
91
|
- 0
|
@@ -85,38 +96,30 @@ dependencies:
|
|
85
96
|
name: sqlite3-ruby
|
86
97
|
prerelease: false
|
87
98
|
requirement: &id006 !ruby/object:Gem::Requirement
|
99
|
+
none: false
|
88
100
|
requirements:
|
89
101
|
- - ">="
|
90
102
|
- !ruby/object:Gem::Version
|
103
|
+
hash: 3
|
91
104
|
segments:
|
92
105
|
- 0
|
93
106
|
version: "0"
|
94
107
|
type: :development
|
95
108
|
version_requirements: *id006
|
96
109
|
- !ruby/object:Gem::Dependency
|
97
|
-
name:
|
110
|
+
name: mysql
|
98
111
|
prerelease: false
|
99
112
|
requirement: &id007 !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
100
114
|
requirements:
|
101
115
|
- - ">="
|
102
116
|
- !ruby/object:Gem::Version
|
117
|
+
hash: 3
|
103
118
|
segments:
|
104
119
|
- 0
|
105
120
|
version: "0"
|
106
121
|
type: :development
|
107
122
|
version_requirements: *id007
|
108
|
-
- !ruby/object:Gem::Dependency
|
109
|
-
name: mysql
|
110
|
-
prerelease: false
|
111
|
-
requirement: &id008 !ruby/object:Gem::Requirement
|
112
|
-
requirements:
|
113
|
-
- - ">="
|
114
|
-
- !ruby/object:Gem::Version
|
115
|
-
segments:
|
116
|
-
- 0
|
117
|
-
version: "0"
|
118
|
-
type: :development
|
119
|
-
version_requirements: *id008
|
120
123
|
description: |-
|
121
124
|
Delayed_job (or DJ) encapsulates the common pattern of asynchronously executing longer tasks in the background. It is a direct extraction from Shopify where the job table is responsible for a multitude of core tasks.
|
122
125
|
|
@@ -176,23 +179,27 @@ rdoc_options:
|
|
176
179
|
require_paths:
|
177
180
|
- lib
|
178
181
|
required_ruby_version: !ruby/object:Gem::Requirement
|
182
|
+
none: false
|
179
183
|
requirements:
|
180
184
|
- - ">="
|
181
185
|
- !ruby/object:Gem::Version
|
186
|
+
hash: 3
|
182
187
|
segments:
|
183
188
|
- 0
|
184
189
|
version: "0"
|
185
190
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
191
|
+
none: false
|
186
192
|
requirements:
|
187
193
|
- - ">="
|
188
194
|
- !ruby/object:Gem::Version
|
195
|
+
hash: 3
|
189
196
|
segments:
|
190
197
|
- 0
|
191
198
|
version: "0"
|
192
199
|
requirements: []
|
193
200
|
|
194
201
|
rubyforge_project:
|
195
|
-
rubygems_version: 1.3.
|
202
|
+
rubygems_version: 1.3.7
|
196
203
|
signing_key:
|
197
204
|
specification_version: 3
|
198
205
|
summary: Database-backed asynchronous priority queue system -- Extracted from Shopify
|