Rails: Cron Job Scheduling using Redis, Resque and Rufus


What is scheduling?

Job SchedulerIn simple terms we can define scheduling as “the process of deciding how to commit resources between a variety of possible tasks“. The scheduling is basically a function which enables us to perform routine tasks at some predefined time or as part of a sequence. These tasks are normally executed in the background by so called “workers”.

Now the question is what’s the best way to run scheduled tasks in a Rails environment? Generally, Rails developers can use an application specific crontab to run application tasks. “Cron” is a time-based job scheduler in Unix-like operating systems (Linux, FreeBSD, Mac OS etc…). And these jobs or tasks are referred to as “Cron Jobs”. Downsides of Cron are:

  • Cron works well on a single server but what if you need to scale to multiple app servers? You’ll need to introduce some kind of lock to avoid concurrency problems. Also, you need to maintain these shared locks.
  • The other problem with Cron is that they are difficult to debug.
  • Cron is for scheduling things, not doing them.

This can almost always be better to place jobs in queue and place the worker system to perform or to execute those jobs. Luckily there is a very good gem named ‘resque‘ available in Ruby on Rails.

Introduction to Resque

Resque is a Redis-backed Ruby library for creating background jobs, placing those jobs on multiple queues and processing them later. The main benefit withResque is that it allows you to create jobs and place them on a queue, then, later, pull those jobs off the queue and process them.

For the backstory, philosophy, and history of Resque’s beginnings, please see the blog post. Besides that recently I came to know about other two plugins which are available in Ruby on Rails for job scheduling. One of them is resque-scheduler and the other is rufus-scheduler. Lets have a look at both both in detail.

Resque Scheduler – A light-weight job scheduling system

Resque Scheduler is an extension to Resque that provides both scheduled and delayed jobs. This service can replace cron and bring the ability to schedule task to execute at certain times. Resque Scheduler support job scheduling in two different ways: Recurring or Scheduled (a standard cron job) and Delayed.

Recurring or Scheduled job

A recurring/scheduled job is a task that is specified to be run at a certain time. For example you may want to update any indexes at midnight everyday; this is a scheduled job that runs based on a fixed schedule which is set at startup. The schedule is just a hash, but is most likely stored in a YAML.
Delayed job
A delayed job is a job that gets put on the queue with a specified time in the future to run at. For example you may want to schedule a followup email to go out a few days after a user has signed up.

How To use Resque-scheduler?

First of all Install Resque Scheduler. Create sample application or you can get the sample application from the github.

$ rails new rescue_jobs -d mysql
$ cd rescue_jobs/

Next step is to modify Gemfile to include

gem 'rails', '3.0.3'
gem 'mysql2'
gem 'resque'
gem 'resque-scheduler'

Run $ bundle install. Now its time to Configure Resque Scheduler. In our sample we use a Redis service, of course you can use your own Redis Server as well. Go to Redis To Go and sign up for the free plan. Once you have an instance, grab the URL given to you and modify the config/initializers/resque.rb as follows:

ENV["REDISTOGO_URL"] ||= "redis://username:password@host:1234/"
uri = URI.parse(ENV["REDISTOGO_URL"])

Resque.redis = Redis.new(:host => uri.host, :port => uri.port, :password => uri.password)
require 'resque_scheduler'

Create a job named eat in app/jobs/eat.rb

module Eat
  @queue = :food
  def self.perform(food)
    puts "Ate #{food}!"
  end
end

Inside config/initializers/resque.rb place the following code so that app/jobs/eat.rb is loaded.

Dir["#{Rails.root}/app/jobs/*.rb"].each { |file| require file }

Create controller file and add code

class EatController 
  "Put #{params[:food]} in fridge to eat later."
end

Add following line in routes.rb file

match 'eat/:food' => 'eat#food'

You also need to add file tasks/resque.rake

Could not embed GitHub Gist 853378: API rate limit exceeded for 178.79.134.61. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)

Finally create the crontab for Resque Scheduler. Create file config/resque_schedule.yml and insert the following text:

Could not embed GitHub Gist 853381: API rate limit exceeded for 178.79.134.61. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)

To load the schedule when we initialize Resque add the following line to config/initializers/resque.rb

Could not embed GitHub Gist 853385: API rate limit exceeded for 178.79.134.61. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)

At the end Start the Scheduler Process

Could not embed GitHub Gist 853392: API rate limit exceeded for 178.79.134.61. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)

Rufus Scheduler – scheduler for Ruby (at, in, cron and every jobs)

Rufus-Scheduler is the latest version of a scheduler previously known as openwferu-scheduler. It is a Ruby gem for scheduling pieces of code (jobs). Rufus-Scheduler understands running a job AT a certain time, IN a certain time, EVERY x time or simply via a CRON statement. But we must note that rufus-scheduler is no replacement for cron since it runs inside of Ruby. The scheduler doesn’t do any server-side tasks to get jobs to run, but relies on its scheduler being run up and maintained persistently (or semi-persistently, as with Rails app processes that will tip the scheduler into action).

It doesn’t require any database table, queueing mechanism, or separate process to manage. Just a simple scheduler to call out to your existing ruby code. No need to register at anything. You have to include only one file in config/initializers and add the task you want to schedule. Whenever you start your server that tasks are done or performed at specific time. So ultimately it saves the time of queuing and you get the work done.

How To use Rufus-Scheduler?

First of all create a sample application.
rails new sample_app

Then add rufus_scheduler in Gemfile
gem ‘rufus-scheduler’

Install the gem with bundle
$ bundle install.

Add following lines in config/initializers/task_scheduler.rb

Could not embed GitHub Gist 854252: API rate limit exceeded for 178.79.134.61. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)

You can also test above code on console.

Conclusion

Rescue Scheduler offers a simple and easy way to add scheduling abilities to any queueing system and is well worth the move from cron. But if you do’t want queuing and want only some scheduled jobs than rufus-scheduler is the best option.

5 thoughts on “Rails: Cron Job Scheduling using Redis, Resque and Rufus

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>