Andy from Webcrunch

Subscribe for email updates:

Portrait of Andy Leverenz
Andy Leverenz

December 5, 2022

Last updated November 5, 2023

How to use Background Jobs with Ruby on Rails

Ruby on Rails is well known for its simplicity and convention over configuration design philosophy. One of the many powerful features of Rails is its support for background jobs, which allow you to run specific tasks asynchronously in the background without blocking the main thread of execution.

Background jobs are especially useful for tasks that take a long time to complete, such as sending emails, processing images, or generating reports. Using background jobs, you can ensure that your application remains responsive and can continue handling incoming requests even while these long-running tasks are being performed.

There are several different ways to implement background jobs in Ruby on Rails. The most common approach is using a gem such as Sidekiq, Delayed Job, or many more, which provides a simple and efficient way to manage your background jobs. These gems make it easy to enqueue jobs, set priorities, and track their progress.

To use a background job gem in your Ruby on Rails application, you must first add it to your Gemfile and run bundle install to install it. Then, you can create a new job by defining a class that inherits from the gem's Job class. For example, if you're using Sidekiq, you might create a new job like this:

rails generate job send_welcome_email
class SendWelcomeEmailJob < Sidekiq::Job
  def perform(user_id)
    user = User.find(user_id)
    UserMailer.welcome_email(user).deliver_later
  end
end

This job will send a welcome email to the user with the specified ID. To enqueue this job, you can use the perform_async method like this:

SendWelcomeEmailJob.perform_async(current_user.id)

This will enqueue the job and add it to the Sidekiq queue, where a worker will pick up and process it. The worker will then run the perform method of the job, which will send the email to the user.

In addition to using a gem like Sidekiq or Delayed Job, you can also use the built-in Active Job framework in Ruby on Rails to manage your background jobs.

To use Active Job, specify which queueing backend you want to use in your config/application.rb file. For example, if you're using Sidekiq, you would add the following line to your configuration file:

config.active_job.queue_adapter = :sidekiq

Then, you can create a new job class that inherits from ActiveJob::Base like this:

class SendWelcomeEmailJob < ActiveJob::Base
  queue_as :default

  def perform(user_id)
    user = User.find(user_id)
    UserMailer.welcome_email(user).deliver_later
  end
end

This job is similar to the one we defined earlier, but it uses the Active Job framework instead of the Sidekiq gem. To enqueue this job, you can use the perform_later method like this:

SendWelcomeEmailJob.perform_later(current_user.id)

This will add the job to the queue, where it will be picked up in order of priority.

If you recall from the snippet before, we added a queue_as :default line to the SendWelcomeEmailJob.

This instructs Sidekiq to give the job default priority, whereas other jobs could take more custom priorities dependent on what is registered inside your application.

To your needs, you can create custom queues relative. Each queuing provider will have different ways of establishing these, but for the most part, they are ad-hoc, meaning add them as necessary.

Best practices

If using something like Sidekiq, it's best to pass as little data as possible through to the given job. Inside the job, you can add additional logic to look up records and perform those heavier tasks.

Additionally, if you make your job idempotent and transactional (a.k.a. capable of being run over and over).

Useful tips and tricks

Sometimes you might want to schedule your background job based on some form of the database entry timestamp. This can be saved as a datetime and referenced using a set function. This means you can depend on a given timeframe to wait until the job gets executed or enter an arbitrary time delay with some simple ruby code.

GuestsCleanupJob.set(wait: 1.week).perform_later(guest) # arbitrary example

Another feature I like to make use of is callback functions. These are available for use depending on a given job's state. I'll commonly have a longer executing job running that might take some time to execute. When that job completes, I email an admin user letting them know that it has been done. It's a valuable way to set it and forget it, making your application work for you while you carry on focus elsewhere.

An example might look like the following, which would go inside your job file.

class MyNeatJob < ApplicationJob
  after_perform do 
    UserMailer.with(admin_id: User.first).job_complete.deliver_later
  end

 # ... more logic
end
Link this article
Est. reading time: 4 minutes
Stats: 4,370 views

Categories