If you want to run longrunning jobs in the background, one very easy solution is using delayed_job. One other very interesting alternative is resque, but it seemed harder to setup (it uses redis), and delayed_job seemed to be just right for my needs.

In short (from the redis documentation) :

Choose Resque if:

  • You need multiple queues
  • You don’t care / dislike numeric priorities
  • You don’t need to persist every Ruby object ever
  • You have potentially huge queues
  • You want to see what’s going on
  • You expect a lot of failure / chaos
  • You can setup Redis
  • You’re not running short on RAM

Choose DelayedJob if:

  • You like numeric priorities
  • You’re not doing a gigantic amount of jobs each day
  • Your queue stays small and nimble
  • There is not a lot failure / chaos
  • You want to easily throw anything on the queue
  • You don’t want to setup Redis

And in my case: a short list of outstanding jobs, i am using a database already, not sure about redis, performance is not that important, i do not expect a lot of failure, i do not want to setup redis (yet :).

So to use the delayed_job gem in a rails3 you will need to use the code from github (as the gem version 2.0.3 is not yet rails3 compatible). Add the following line to your Gemfile :

gem "delayed_job",  :git => 'git://github.com/collectiveidea/delayed_job.git'

and then run

bundle install

Because i am using ActiveRecord, i can just use the generator, that will create the table and add the script to run a worker:

rails generate delayed_job
rake db:migrate

Include the rake tasks from delayed-jobs into your Rakefile:

begin
  require 'delayed/tasks'
rescue LoadError
  STDERR.puts "Run `bundle:install` to install delayed_job"
end

Create an initializer file delayed_jobs_config.rb and write

# config/initializers/delayed_job_config.rb
Delayed::Worker.destroy_failed_jobs = false
#Delayed::Worker.sleep_delay = 60
#Delayed::Worker.max_attempts = 3
Delayed::Worker.max_run_time = 5.minutes

If you are happy with the defaults, you can leave it. For me it was important to keep a record of the jobs that failed.

Then you can just delay any function-call like this:

envelope.delay.query_status(delivery)

Just adding the .delay does all the magic for you! And starting a worker is as simple as

rake jobs:work