Ruby on Rails - Active Job



The Active Job framework in Rails is extremely helpful for declaring background jobs and executing them asynchronously.

Applications that involve background tasks such as sending emails, pushing notifications, background processing of CSV files and backing up the databases, integration with external apps through APIs, handling file uploads, etc., can be scheduled with Active Job framework.

Emails with Active Job

As explained in the previous chapter (Ruby on Rails – Sending Emails), there is a welcome_email() method in the UserMailer class.

class UserMailer < ApplicationMailer
  default from: " GMAIL_USERNAME"

  def welcome_email(user)
    @user = user
    mail(to: @user.email, subject: "Welcome to My Awesome Site!")
  end
end

Next, you need to create an Active Job with the following command:

rails generate job send_emails

This creates a custom job (SendEmailsJob) to handle the email-sending process asynchronously.

Active Job provides a default perform() method which you need to override to call the welcome_email() method from the UserMailer class.

class SendEmailsJob < ApplicationJob
  queue_as :default  

  def perform(user)
    UserMailer.welcome_email(user).deliver
  end
end

You now need to install a queuing service to handle the background jobs. We have a variety of background queuing libraries such as delayed jobs, sidekiq, rescue, and so on.

We will be using Sidekiq for the active job. For that, we must add a gem of sidekiq into Gemfile:

gem 'sidekiq'
bundle install

However, you need to let Rails know that this gem is to be used for background processing. So, edit your application.rb to set the active_job.queue_adapter.

module MyApplication
  class Application < Rails::Application
    # make sure the adapter's gem has been included in your Gemfile.
    config.active_job.queue_adapter = :sidekiq
  end
end

Enqueuing the job

After configuring queueing backend, you must add your job to the queue. Enqueue a job for execution when the queuing system is available. Along with it, the queue will change whenever the time changes. You may set times for when a task needs to be completed after a month or to be executed as soon as possible.

Considering that SendEmailsJob is the name of our job, the multiple techniques listed below can be used to achieve enqueuing options.

wait Enqueues the job with the specified delay
wait_until Enqueues the job at the time specified
queue Enqueues the job on the specified queue
priority Enqueues the job with the specified priority

For example,

SendEmailsJob.set(wait: 1.week).perform_later(user)

This statement schedules sending the email 1 week from now. Or,

SendEmailsJob.set(wait_until: Date.tomorrow.noon).perform_later(user)

Will start the job tomorrow at noon.

Recent versions of Ruby on Rails do not require you to generate a custom job when you're just using ActionMailer with deliver_later. To enable Active Job in your Rails application for sending emails asynchronously (such as when a User is created), you just need to queue the email delivery job instead of sending it directly.

Edit your UserController and modify the create action as below:

def create
    @user = User.new(user_params)
    if @user.save
      # UserMailer.welcome_email(@user).deliver_now  # Send email after user creation
      UserMailer.welcome_email(@user).deliver_later  # Send email later
      redirect_to @user, notice: "User was successfully created. A welcome email has been sent."
    else
      render :new, status: :unprocessable_entity
    end
  end

You can specify the wait parameter to deliver_later() method such as −

UserMailer.welcome_email(user).deliver_later(wait: 10.minutes)

Or,

UserMailer.welcome_email(user).deliver_later(wait_until: Time.tomorrow.noon)

Callbacks

The Active Job framework has certain hooks so that a defined logic can be triggered at various points during a job’s life cycle. These are the available callbacks for active jobs:

Callback Description
before_enqueue Defines a callback that will get called right before the job is enqueued.
around_enqueue Defines a callback that will get called around the enqueuing of the job.
after_enqueue Defines a callback that will get called right after the job is enqueued.
before_perform Defines a callback that will get called right before the job’s perform method is executed.
around_perform Defines a callback that will get called around the job’s perform method.
after_perform Defines a callback that will get called right after the job’s perform method has finished.

Retrying or Discarding Failed Jobs

It's possible to retry or discard a failed job by using retry_on or discard_on, respectively. If the configuration doesn't specify, a failed job will not be retried. Using retry_on or discard_on, respectively, you can attempt again after a failed job. And default retry_on is to 3 seconds and 5 attempts.

Advertisements