Skip to content

Commit

Permalink
respond to cron changes, while job is running and otherwise
Browse files Browse the repository at this point in the history
  • Loading branch information
codez committed May 26, 2016
1 parent 99ea6bd commit ca2cbc2
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 15 deletions.
9 changes: 7 additions & 2 deletions lib/delayed_cron_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require 'delayed_cron_job/cronline'
require 'delayed_cron_job/plugin'
require 'delayed_cron_job/version'
require 'delayed_cron_job/backend/updatable_cron'

module DelayedCronJob

Expand All @@ -11,10 +12,14 @@ module DelayedCronJob
if defined?(Delayed::Backend::Mongoid)
Delayed::Backend::Mongoid::Job.field :cron, :type => String
Delayed::Backend::Mongoid::Job.attr_accessible(:cron) if Delayed::Backend::Mongoid::Job.respond_to?(:attr_accessible)
Delayed::Backend::Mongoid::Job.send(:include, DelayedCronJob::Backend::UpdatableCron)
end

if defined?(Delayed::Backend::ActiveRecord) && Delayed::Backend::ActiveRecord::Job.respond_to?(:attr_accessible)
Delayed::Backend::ActiveRecord::Job.attr_accessible(:cron)
if defined?(Delayed::Backend::ActiveRecord)
Delayed::Backend::ActiveRecord::Job.send(:include, DelayedCronJob::Backend::UpdatableCron)
if Delayed::Backend::ActiveRecord::Job.respond_to?(:attr_accessible)
Delayed::Backend::ActiveRecord::Job.attr_accessible(:cron)
end
end

Delayed::Worker.plugins << DelayedCronJob::Plugin
Expand Down
17 changes: 17 additions & 0 deletions lib/delayed_cron_job/backend/updatable_cron.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module DelayedCronJob
module Backend
module UpdatableCron

def self.included(klass)
klass.send(:before_save, :set_next_run_at, :if => :cron_changed?)
end

def set_next_run_at
if cron.present?
self.run_at = Cronline.new(cron).next_time(Delayed::Job.db_time_now)
end
end

end
end
end
16 changes: 7 additions & 9 deletions lib/delayed_cron_job/plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,12 @@ module DelayedCronJob
class Plugin < Delayed::Plugin

class << self
def next_run_at(job)
job.run_at = Cronline.new(job.cron).next_time(Delayed::Job.db_time_now)
end

def cron?(job)
job.cron.present?
end
end

callbacks do |lifecycle|
# Calculate the next run_at based on the cron attribute before enqueue.
lifecycle.before(:enqueue) do |job|
next_run_at(job) if cron?(job)
end

# Prevent rescheduling of failed jobs as this is already done
# after perform.
Expand All @@ -39,6 +31,13 @@ def cron?(job)
end
end

# Update the cron expression from the database in case it was updated.
lifecycle.after(:invoke_job) do |job|
if cron?(job)
job.cron = job.class.where(:id => job.id).pluck(:cron).first
end
end

# Schedule the next run based on the cron attribute.
lifecycle.after(:perform) do |worker, job|
if cron?(job)
Expand All @@ -48,7 +47,6 @@ def cron?(job)
next_job.locked_at = nil
next_job.locked_by = nil
next_job.attempts += 1
next_run_at(next_job)
next_job.save!
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/delayed_cron_job/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module DelayedCronJob
VERSION = '0.7.0'
VERSION = '0.7.1'
end
36 changes: 33 additions & 3 deletions spec/delayed_cron_job_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def perform; end

it 'schedules a new job after success' do
job.update_column(:run_at, now)
job.reload
job.reload # adjusts granularity of run_at datetime

worker.work_off

Expand All @@ -48,7 +48,7 @@ def perform; end
it 'schedules a new job after failure' do
allow_any_instance_of(TestJob).to receive(:perform).and_raise('Fail!')
job.update(run_at: now)
job.reload
job.reload # adjusts granularity of run_at datetime

worker.work_off

Expand Down Expand Up @@ -98,7 +98,7 @@ def perform; end
expect(j.last_error).to eq(nil)
end

it 'has correct last_error after success' do
it 'has updated last_error after failure' do
allow_any_instance_of(TestJob).to receive(:perform).and_raise('Fail!')
job.update(run_at: now, last_error: 'Last error')

Expand Down Expand Up @@ -138,6 +138,36 @@ def perform; end
j = Delayed::Job.first
expect(j.attempts).to eq(job.attempts + 1)
end

it 'updates run_at if cron is changed' do
job.update!(cron: '1 10 * * *')
expect(job.run_at.min).to eq(1)
end

it 'uses new cron when this is updated while job is running' do
job.update_column(:run_at, now)
allow_any_instance_of(TestJob).to receive(:perform) { job.update!(cron: '1 10 * * *') }

worker.work_off

j = Delayed::Job.first
expect(j.run_at.min).to eq(1)
end

it 'does not reschedule job if cron is cleared while job is running' do
job.update_column(:run_at, now)
allow_any_instance_of(TestJob).to receive(:perform) { job.update!(cron: '') }

expect { worker.work_off }.to change { Delayed::Job.count }.by(-1)
end

it 'does not reschedule job if model is deleted while job is running' do
job.update_column(:run_at, now)
allow_any_instance_of(TestJob).to receive(:perform) { job.destroy! }

expect { worker.work_off }.to change { Delayed::Job.count }.by(-1)
end

end

context 'without cron' do
Expand Down

0 comments on commit ca2cbc2

Please sign in to comment.