Skip to content

Instantly share code, notes, and snippets.

@freddyb2
Forked from Diasporism/redis_and_resque.mkd
Created August 5, 2021 18:33
Show Gist options
  • Save freddyb2/4e1027375e8f08b2c68d822f0809a272 to your computer and use it in GitHub Desktop.
Save freddyb2/4e1027375e8f08b2c68d822f0809a272 to your computer and use it in GitHub Desktop.
How to configure Redis and Resque for your Rails app.

Redis and Resque

What this guide will cover: the code you will need in order to include Redis and Resque in your Rails app, and the process of creating a background job with Resque.

What this guide will not cover: installing Ruby, Rails, or Redis.

Note: As of this writing I am still using Ruby 1.9.3p374, Rails 3.2.13, Redis 2.6.11, and Resque 1.24.1. I use SQLite in development and Postgres in production.

Background jobs are frustrating if you've never dealt with them before. Over the past few weeks I've had to incorporate Redis and Resque into my projects in various ways and every bit of progress I made was very painful. There are many 'gotchas' when it comes to background workers, and documentation tends to be outdated or scattered at best.

The first thing you need to know about setting up Redis and Resque is that things change depending on your environment. You can get away with very minimal setup for a development environment, but production environments such as Heroku require extra steps which can often be frustrating to find.

So let's get started with the steps to configure Redis and Resque in your Rails app.

Setup

Add the following gems to your Gemfile and bundle:

gem 'redis'
gem 'resque', require: 'resque/server'

Note: 'resque-scheduler' is an optional plugin for rescue which allows you to easily setup scheduled jobs. You don't need it if you just want to move basic processes into background jobs and queue them up when certain events happen in your code.

Documentation for these gems:

Adding these gems to our gem file will give us the tools we need to interact with Redis and Resque in in our code.

From here we only need to add a couple lines of code in a few different files. Starting off, we will need rake tasks to start our schedules and workers.

Create a lib/tasks/resque.rb file and add the following to it:

require 'resque/tasks'

namespace :resque do
  task :setup do
    require 'resque'
    ENV['QUEUE'] = '*'

    Resque.redis = 'localhost:6379' unless Rails.env == 'production'
  end
end

Resque.after_fork = Proc.new { ActiveRecord::Base.establish_connection } #this is necessary for production environments, otherwise your background jobs will start to fail when hit from many different connections.

desc "Alias for resque:work (To run workers on Heroku)"
task "jobs:work" => "resque:work"

The important bit is "Resque.redis = 'localhost:6379' unless Rails.env == 'production'". This line tells Rails that we're running Redis on port 6379 (which is the Redis default) unless were running our app in production. Great, but where do we tell it to look if we are running in production? That's next.

Create a config/initializers/redis.rb file and add this to it:

if Rails.env == 'production'
  uri = URI.parse(ENV["REDISTOGO_URL"])
  Resque.redis = Redis.new(:host => uri.host, :port => uri.port, :password => uri.password)
end

Notice that we are using an environment variable to hold our Redis url for us. Also note this block of code is only going to be run when our environment is production. That's fine because we already told Rails where to look for Redis for every other environment.

And finally, we need to actually set the environment variable. We do this in config/environments/production.rb by adding the following line inside the AppName::Application.configure block like so:

YourAppNameHere::Application.configure do
  ENV["REDISTOGO_URL"] = 'redis://redistogo:[email protected]:1234/'

  #other stuff below
end

For the sake of clarity, the above Redis url is bogus. You'll need to replace it with a real one, which you can get for free by provisioning your Heroku app with Redis To Go Nano. When you visit your Redis To Go account, it should tell you what your url is.

Last but not least, we'll need a Procfile with some commands in it so we don't need to manually startup workers and schedulers on Heroku.

Create a Procfile at the base of your application (if you don't already have one) and add the following lines to it:

web: bundle exec rails server -p $PORT
worker: QUEUE=* bundle exec rake environment resque:work

And that's it for setup. Your app is now ready to create and schedule background jobs and workers in both production (assuming your using Heroku) and development.

Creating Background Jobs

Lets say we have a Rails app that sends an email out every time a user creates an account. If we had the code to send the email in our controller or model, the user would actually have to wait for Rails to send the email before he could continue after creating an account. This can be bad if the process of sending an email is slow (which it generally is).

Let's fix this by moving the code to send an email on account creation into a background job.

Create a app/jobs directory in your Rails app. This is where you will keep all your job classes. Inside the jobs directory, create an email_sender.rb file.

class EmailSender
  @queue = :emails_queue

  def self.perform(params)
    #code to send out emails
  end
end

Stick all the of the code you had to send the email in the self.perform method (you can pass in any parameters you need into this method, it's just a Ruby method after all.) Then, in place of where you used to keep the code for sending emails, put this:

Resque.enqueue(EmailSender, params[user_id: current_user.id])

Simple. Not only does it make your code look cleaner by moving responsibility out of controllers and models, but it also means that the code will execute in the background and the user will not have to wait for it to complete before he can continue.

Scheduled Jobs and Delayed Jobs

There is a gem called 'resque-scheduler' (https://github.com/bvandenbos/resque-scheduler) which allows for easily setting up delayed and scheduled jobs with Resque. It also has some decent documentation on getting it up and running and a couple example jobs to look at.

If you want to include it, add it to your gemfile and change your lib/tasks/resque.rake file to the following:

require 'resque/tasks'
require 'resque_scheduler/tasks'

namespace :resque do
  task :setup do
    require 'resque'
    require 'resque_scheduler'
    require 'resque/scheduler'

    ENV['QUEUE'] = '*'

    Resque.redis = 'localhost:6379' unless Rails.env == 'production'
    Resque.schedule = YAML.load_file(File.join(Rails.root, 'config/resque_scheduler.yml'))
  end
end

Resque.after_fork = Proc.new { ActiveRecord::Base.establish_connection }

desc "Alias for resque:work (To run workers on Heroku)"
task "jobs:work" => "resque:work"

Create a config/resque_scheduler.yml file and add all your scheduled jobs to it. Finally, add this line to your Procfile to include booting up scheduler automatically:

scheduler: bundle exec rake resque:scheduler

FIN

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment