To leverage Delayed Job in Heroku, you’ll need for follow a few steps. In my case, since I’m using Mongoid, I’m going to use an alternate Active Record Delayed Job tie-in dubbed delayed_job_mongoid
. The reason for this Active Record integration will become obvious once I show you how to actually force a long running task to be asynchronous. Regardless if you are using Mongoid or not, the Active Record integration is identical.
First and foremost, you’ll need to add delayed_job_mongoid
to your Gemfile
(or if you aren’t using Mongoid, add delayed_job_active_record
). Delayed Job stores jobs in your database; consequently, after you run bundle install
, proceed to update your underlying MongoDB instance with a new index by running:
|
|
Obviously, if you’re using a normal RDMBS, your command will be slightly different (i.e. rake db:migrate
).
After that step is complete, you’ll need to generate a delayed_job
script via:
|
|
This script allows to you execute the Delayed Job framework with a number of options. As it turns out, you don’t really need this script as Delayed Job hooks into Rake as well; it is via the Rake integration that Heroku will work with Delayed Job.
Now that you’ve set up 80% of the infrastructure required to support Delayed Job, you next need to background some long running task. This is the fun part and like everything else so far, it couldn’t be any easier.
Delayed Job hooks directly into Active Record and thus, you can background some unit of work on a model object by inserting a delay
call. For example, in my case, I had a Group
model object that when updated under certain circumstances required that all associated users also be updated (wouldn’t it be nice if MongoDB supported triggers?). As you can imagine, updating hundreds of users is rather time intensive.
Thus, the user update logic was thrown into a special method (dubbed aysnc_update_users
) and was then invoked like so:
|
|
This call essentially serializes the logic here into a collection called delayed_backend_mongoid_jobs
which can then be invoked asynchronously by another process. And therein lies your last few steps.
Heroku has the notion of worker dynos, which are background processors – think of worker dynos as engines capable of doing what ever you want them to do. In the case of Delayed Job, you want those worker dynos to invoke jobs residing in the delayed_backend_mongoid_jobs
collection (which is conceptually a queue).
To configure a worker dyno, you’ll need to do 2 things. First, provision one like so:
|
|
You’ll then need to update your Procfile
to include the command the worker dyno should invoke:
|
|
As you can see, the worker dyno in this case is invoking a Rake task (jobs:work
) which is asynchronously polling for new jobs in your conceptual queue and invoking them as they are found.
Finally, deploy to Heroku and keep an eye on the queue for jobs – you can isolate the dyno workers logs via
|
|
Was that easy or what?
This story, "Backgrounding tasks in Heroku with Delayed Job" was originally published by JavaWorld.