Skip to content

Commit

Permalink
Reschedule jobs using a time provided by the handler, or fall back to…
Browse files Browse the repository at this point in the history
… default
  • Loading branch information
betamatt committed Sep 23, 2010
1 parent 234fcf8 commit 1fea4b0
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 5 deletions.
7 changes: 6 additions & 1 deletion lib/delayed/backend/base.rb
Expand Up @@ -96,12 +96,17 @@ def hook(name, *args)
end
end

def reschedule_at
payload_object.respond_to?(:reschedule_at) ?
payload_object.reschedule_at(self.class.db_time_now, attempts) :
self.class.db_time_now + (attempts ** 4) + 5
end

protected

def set_default_run_at
self.run_at ||= self.class.db_time_now
end

end
end
end
14 changes: 11 additions & 3 deletions lib/delayed/backend/shared_spec.rb
Expand Up @@ -294,7 +294,7 @@ def create_job(opts = {})
@job.locked_at.should be_nil
end
end

context "large handler" do
before do
text = "Lorem ipsum dolor sit amet. " * 1000
Expand Down Expand Up @@ -402,8 +402,8 @@ def create_job(opts = {})
# reset defaults
Delayed::Worker.destroy_failed_jobs = true
Delayed::Worker.max_attempts = 25

@job = Delayed::Job.enqueue ErrorJob.new
@job = Delayed::Job.enqueue(ErrorJob.new)
end

it "should record last_error when destroy_failed_jobs = false, max_attempts = 1" do
Expand All @@ -425,6 +425,14 @@ def create_job(opts = {})
@job.run_at.should > Delayed::Job.db_time_now - 10.minutes
@job.run_at.should < Delayed::Job.db_time_now + 10.minutes
end

it 'should re-schedule with handler provided time if present' do
@job = Delayed::Job.enqueue(CustomRescheduleJob.new(99.minutes))
@worker.run(@job)
@job.reload

(Delayed::Job.db_time_now + 99.minutes - @job.run_at).abs.should < 1
end
end

context "reschedule" do
Expand Down
2 changes: 1 addition & 1 deletion lib/delayed/worker.rb
Expand Up @@ -128,7 +128,7 @@ def run(job)
# Uses an exponential scale depending on the number of failed attempts.
def reschedule(job, time = nil)
if (job.attempts += 1) < self.class.max_attempts
time ||= Job.db_time_now + (job.attempts ** 4) + 5
time ||= job.reschedule_at
job.run_at = time
job.unlock
job.save!
Expand Down
6 changes: 6 additions & 0 deletions spec/sample_jobs.rb
Expand Up @@ -14,6 +14,12 @@ class ErrorJob
def perform; raise 'did not work'; end
end

class CustomRescheduleJob < Struct.new(:offset)
cattr_accessor :runs; self.runs = 0
def perform; raise 'did not work'; end
def reschedule_at(time, attempts); time + offset; end
end

class LongRunningJob
def perform; sleep 250; end
end
Expand Down
51 changes: 51 additions & 0 deletions spec/worker_spec.rb
Expand Up @@ -34,4 +34,55 @@
lambda { Delayed::Worker.guess_backend }.should_not change { Delayed::Worker.backend }
end
end

describe "running a job" do
before(:each) do
@worker = Delayed::Worker.new
end

after(:each) do
Delayed::Job.delete_all
end

describe 'that fails' do
before(:each) do
@handler = ErrorJob.new
@job = Delayed::Job.enqueue(@handler)
end

it 'should increase the attempts' do
@worker.run(@job)
@job.attempts.should == 1
end

it 'should reschedule the job in the future' do
@worker.run(@job)
@job.run_at.should > Job.db_time_now + 5
end

describe 'with custom rescheduling strategy' do
before(:each) do
@reschedule_at = Time.current + 7.hours
@handler.stub!(:reschedule_at).and_return(@reschedule_at)
end

it 'should invoke the strategy' do
@handler.should_receive(:reschedule_at) do |time, attempts|
(Job.db_time_now - time).should < 2
attempts.should == 1

Job.db_time.now + 5
end

@worker.run(@job)
end

end

it 'should reschedule at the specified time' do
@worker.run(@job)
@job.run_at.should == @reschedule_at
end
end
end
end

0 comments on commit 1fea4b0

Please sign in to comment.