RecurringJob
RecurringJob creates a framework for creating custom DelayedJob jobs that are automatically rescheduled to run again.
Installation
RecurringJob requires delayed_job_active_record ( > 4.0). Follow the instructions to install DelayedJob first.
Then add this line to your application's Gemfile:
gem 'recurring_job'
And then execute:
$ bundle
Or install it yourself as:
$ gem install recurring_job
By default, RecurringJob logs to STDOUT. If you want it to log to your rails logger, put the following somewhere in your rails configuration files (I use environment.rb)
RecurringJob.logger = Rails.logger
Usage
RecurringJob extends the functionality of Custom Jobs within DelayedJob. It uses the job's queue field to identify each type of recurring job, and to ensure a single instance of each one is scheduled in the job queue at a time.
To use RecurringJob, you need to create a custom job class for each type of job you wish to schedule.
I like to put job classes in a lib/jobs
folder in my rails app, but you're free to put them anywhere you like.
This example, which is similar to how we use RecurringJob at OnLive, shows how to subclass the RecurringJob class and provide a perform
method that does the actual work.
We can send in our own options (in this example an app_id
for a database Model named App) and those will be passed on each
time when the job is scheduled. In addition we are automatically passed in the job id of the DelayedJob job, which in this
case we use for locking (Of course, you can also just ignore the job id if you don't need it!).
class AppStatusJob < RecurringJob
def perform
return unless
app_id = [:app_id]
recurring_job_id = [:delayed_job_id]
apps_to_process = app_id ? App.where(id:app_id) : App.all
apps_to_process.each do |app|
app.lock_for_status_check(recurring_job_id) do
# if no one else is modifying the app right now
# this block gets executed
app.do_whatever_it_means_to_check_status
end
end
end
def after(job)
super # have to allow RecurringJob to do its work!!
send_email_about_job_success(job)
end
end
We can run this job a single time to check a single app
AppStatusJob.queue_once(app_id:App.first.id)
Or we can set it up to run as a scheduled job to check all apps every hour
AppStatusJob.schedule_job(interval:1.hour)
Or we can set it up to run for each particular app on a schedule, using the (unique) name of the app as the name of the queue
App.all.each do |app|
AppStatusJob.schedule_job(interval:1.hour, app_id:app.id, queue:app.name)
end
Once AppStatusJob has been set up as a RecurringJob, the scheduled jobs will automatically add themselves back into the queue to run an hour after they finish (or whatever interval you choose), continuing indefinitely! And like all DelayedJobs, you can specify actions to happen when these jobs succeed, or fail, and the jobs live in the DelayedJob queue between runs. (See more info about DelayedJob hooks).
Important: If you implement any of the DelayedJob hooks (before
, after
, success
, error
, failure
, or enqueue
) in your RecurringJob, you must call super to allow the RecurringJob hooks
to do its work!
More info
If you want to change the interval of a job, you can call schedule_job again and it will change the currently scheduled job.
AppStatusJob.schedule_job(interval:30.minutes)
You can stop a job from running anymore by taking it out of the queue
AppStatusJob.unschedule_job
To get a list of all the RecurringJobs you have scheduled
RecurringJob.all
Another example
You might want to use RecurringJob to send emails in batches, for example if your email service has limits to the number of API calls per day, like iPost does. If you had a queue of pending emails in a table called pending_emails, it might look something like this (all actual email details left as an exercise for the reader).
class BatchEmailJob < RecurringJob
def perform
# send any pending emails
@email_status_hash = PendingEmail.send_all # returns a hash of # emails were sent, any failures, etc
end
def success(job)
super # allow RecurringJob to do its work!!
notify_job_succeeded(@email_status_hash)
end
def max_attempts
3
end
end
And then set it up to run as often as you want to send the emails, say every 5 hours.
BatchEmailJob.schedule_job(interval:5.hours)
Contributing
- Fork it ( https://github.com/[my-github-username]/recurring_job/fork )
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request
License and Copying
Recurring Job is released under the MIT License by OL2, Inc. Please see the file LICENSE.txt for a copy of this license.