by Audrey Carlsen, cohort 1 student. Read the full post on her blog.
I spent last Friday trying to push my most recent web app to production. The biggest challenge was figuring out how to implement a gem called Resque, which allows you to run background jobs (such as mailing all users when a new blog post is created) without having the hold up the rest of your app. In this post, I’m going to assume that you already have Resque working locally and that you are now trying to push your app to Heroku.
(For full disclosure, I could not have gotten Resque working on Heroku without the incredible amount of time and energy that one of my classmates put into figuring out this problem. Huge props to her.)
The first thing you’re going to need to do is get an external Redis server up and running for your app to use in production. To do this, sign up for an account with RedisToGo. Select the free hosting plan and create a new Redis “instance.” RedisToGo will provide you with a URL containing all of the information your app will need to know about this instance, including host, port, and password. Create a “redis.rb” initializer file in your config directory, containing the following code:
ENV['REDISTOGO_URL'] ||= "redis://username:password@host:6379/" if Rails.env.production? uri = URI.parse(ENV['REDISTOGO_URL']) Resque.redis = Redis.new(:host => uri.host, :port => uri.port, :password => uri.password) elsif Rails.env.development? Resque.redis = Redis.new(:host => 'localhost', :port => '6379') end
Basically, this is letting your application know which instance of the Redis server to use based on which environment it is running in. If it is running in production, it should be using the instance provided by RedisToGo, and if it is running in development, it should be using your local Redis server (I believe the port is 6379 for everyone, but don’t quote me on that).
Now that we’ve got Redis set up, let’s move on to Resque itself. Heroku lets you add workers to your app, but it’s going to charge you for it. The best way to implement Resque on Heroku is to use an autoscaler that only adds a worker when it’s needed, so that you are only charged for the brief time that it takes for the worker to do its job (this amounts to a couple of cents).
There are several Heroku autoscaler gems out there. I used this one.
First, add the gem to your Gemfile. Note that you might also have to specify an older version of Resque in order for it to be compatible with this new gem:
gem 'resque', '~> 1.23.0' gem 'heroku_resque_autoscaler'
This gem has fairly straightforward documentation, so I’m not going to repeat all of the instructions here. Basically, you’ll need to create another intiailizer for the autoscaler, including information about your Heroku API key and app name. (The app name should just be the name itself, i.e. the part preceding “.herokuapp.com” in the URL.) You will also need to make a small change to your worker files so that they “extend” the autoscaler (don’t ask me what that means, but it seems to be essential).
Once you do everything the documentation tells you to do, you’re still going to need to add one more thing. (Why the gem doesn’t include this in its documentation is beyond me.) If you don’t already have one, create a “Procfile” in the main directory of your app (same place you’d put a Gemfile or Rakefile), and add the following code (WARNING: THIS IS WHERE THINGS START TO GET REALLY HAND-WAVY FOR ME):
worker: env TERM_CHILD=1 QUEUE=* bundle exec rake resque:work
What the hell is a Procfile?, you might ask. Good question. This is the first time I’ve ever encountered such a thing. As far as I can gather, the purpose of the Procfile is to define any “processes” (e.g. servers that are running, Resque workers, etc.) that Heroku needs to know about. Basically, this code is defining the worker process for Heroku, using the same command that you would call in the console to start Resque. TERM_CHILD=1
is optional (but a pretty good idea to include) – it ensures that workers have time to execute their processes before shutting down, as opposed to just terminating the worker.
And there you have it! A quick and dirty way to get Resque working without dishing out a ton of money.