Handle a few jobs scheduled at the same time

This commit is contained in:
Dzmitry Plashchynski
2015-04-13 15:53:20 +03:00
parent a28ec7b276
commit 6d41a19212
2 changed files with 24 additions and 13 deletions

View File

@@ -6,18 +6,20 @@ module Crono
class Job class Job
include Logging include Logging
attr_accessor :performer, :period, :last_performed_at, :job_log, attr_accessor :performer, :period, :last_performed_at,
:job_logger, :healthy :next_performed_at, :job_log, :job_logger, :healthy
def initialize(performer, period) def initialize(performer, period)
self.performer, self.period = performer, period self.performer, self.period = performer, period
self.job_log = StringIO.new self.job_log = StringIO.new
self.job_logger = Logger.new(job_log) self.job_logger = Logger.new(job_log)
self.next_performed_at = period.next
@semaphore = Mutex.new @semaphore = Mutex.new
end end
def next def next
period.next(since: last_performed_at) return next_performed_at if next_performed_at.future?
Time.now
end end
def description def description
@@ -31,6 +33,7 @@ module Crono
def perform def perform
log "Perform #{performer}" log "Perform #{performer}"
self.last_performed_at = Time.now self.last_performed_at = Time.now
self.next_performed_at = period.next(since: last_performed_at)
Thread.new { perform_job } Thread.new { perform_job }
end end
@@ -44,6 +47,7 @@ module Crono
def load def load
self.last_performed_at = model.last_performed_at self.last_performed_at = model.last_performed_at
self.next_performed_at = period.next(since: last_performed_at)
end end
private private
@@ -61,23 +65,24 @@ module Crono
def perform_job def perform_job
performer.new.perform performer.new.perform
finished_time_sec = format('%.2f', Time.now - last_performed_at)
rescue StandardError => e rescue StandardError => e
handle_job_fail(e, finished_time_sec) handle_job_fail(e)
else else
handle_job_success(finished_time_sec) handle_job_success
ensure ensure
save save
end end
def handle_job_fail(exception, finished_time_sec) def handle_job_fail(exception)
finished_time_sec = format('%.2f', Time.now - last_performed_at)
self.healthy = false self.healthy = false
log_error "Finished #{performer} in #{finished_time_sec} seconds"\ log_error "Finished #{performer} in #{finished_time_sec} seconds"\
"with error: #{exception.message}" " with error: #{exception.message}"
log_error exception.backtrace.join("\n") log_error exception.backtrace.join("\n")
end end
def handle_job_success(finished_time_sec) def handle_job_success
finished_time_sec = format('%.2f', Time.now - last_performed_at)
self.healthy = true self.healthy = true
log "Finished #{performer} in #{finished_time_sec} seconds" log "Finished #{performer} in #{finished_time_sec} seconds"
end end

View File

@@ -35,16 +35,22 @@ describe Crono::Scheduler do
expect(jobs).to be_eql [jobs[0], jobs[1]] expect(jobs).to be_eql [jobs[0], jobs[1]]
end end
it 'should return an array of jobs scheduled at same time without `at`' do it 'should handle a few jobs scheduled at same time without `at`' do
time = 5.minutes.from_now
scheduler.jobs = jobs = [ scheduler.jobs = jobs = [
Crono::Period.new(10.seconds), Crono::Period.new(10.seconds),
Crono::Period.new(10.seconds), Crono::Period.new(10.seconds),
Crono::Period.new(1.day, at: 10.minutes.from_now.strftime('%H:%M')) Crono::Period.new(1.day, at: 10.minutes.from_now.strftime('%H:%M'))
].map { |period| Crono::Job.new(TestJob, period) } ].map { |period| Crono::Job.new(TestJob, period) }
time, jobs = scheduler.next_jobs _, next_jobs = scheduler.next_jobs
expect(jobs).to be_eql [jobs[0], jobs[1]] expect(next_jobs).to be_eql [jobs[0]]
Timecop.travel(4.seconds.from_now)
expect(Thread).to receive(:new)
jobs[0].perform
_, next_jobs = scheduler.next_jobs
expect(next_jobs).to be_eql [jobs[1]]
end end
end end
end end