Rails

Active Job


Hey guys! I hope you are doing great. Recently I worked on a Spree extension to index products to an Elasticsearch server. To do this, I used an enqueued job to send the model to Elasticsearch. In the outside world, you can use different queueing backends to accomplish this, like Sidekiq, Delayed Job, etc.

Since I was developing an extension, I wanted to make it work with any queueing backend, or at least, with several ones. That’s where Active Job comes handy.

What is Active Job?

From the Rails guides:

“Active Job is a framework for declaring jobs and making them run on a variety of queueing backends. These jobs can be everything from regularly scheduled clean-ups, to billing charges, to mailings. Anything that can be chopped up into small units of work and run in parallel, really.”

Rails 4.2

This ability comes out of the box in Rails 4.2. To create a job, you just do the following:

$ rails g job elasticsearch_index

With this, Rails will create a Ruby file. In this case, it will create elasticsearch_index_job.rb file inside app/jobs folder.

Here’s how our job file looks like:


class ElasticsearchIndexJob < ActiveJob::Base
  queue_as :default

  def perform(*args)
    # Do something later
  end
end

Active Job for Rails version below 4.2

If your Rails app uses a Rails version lower than 4.2, you can still use Active Job. For this, you just add the gem to your Gemfile.

gem 'activejob'

And don’t forget to install it.

$ bundle install

After this, you need to create your job file manually. Don’t forget to put it inside app/jobs and to make it extends from ActiveJob::Base.

Using Active Job

There are some differences between using Active Job in Rails 4.2 and lower versions.

Rails 4.2

Equeueing a job

To enqueue our job, we just simply do:

MyCustomJob.perform_later record

This will perform our job as soon as a queueing system is free. To perform it in a given time, you can do this:

MyCustomJob.set(wait_until: Date.tomorrow.noon).perform_later(record)
# or
MyCustomJob.set(wait: 1.week).perform_later(record)

Set a queueing backend

By default, Active Job executes the jobs immediately (inline option). To use a queueing backend, is necessary to set it inside our config/application.rb file.

config.active_job.queue_adapter = :sidekiq
# or
config.active_job.queue_adapter = :delayed_job

Here you can find the list of queueing backends that Active Job supports.

Lower versions

As I said before, implementing Active Job for lower versions changes a little bit.

Equeueing a job

To enqueue our job, we just simply do:

MyCustomJob.enqueue record

As you can see, to enqueues jobs we use enqueue method instead of perform_later.

Enqueue a job to be performed at an interval from now:

MyCustomJob.enqueue_in(1.week, record)

Enqueue a job to be performed at an explicit point in time:

MyCustomJob.enqueue_at(Date.tomorrow.midnight, record)

Set a queueing backend

Another difference is setting a queueing backend. To do this, we need to create an initializer.

# config/initializers/active_job.rb

require 'active_job'
# or any other supported backend such as :sidekiq or :delayed_job
ActiveJob::Base.queue_adapter = :inline

This is crucial because it’s necessary to have this initializer with at least an :inline queue adapter. You may notice the require 'active_job' line, that’s because Active Job doesn’t load itself into our Rails app by default.

Note For lower versions, there are no callback mechanisms such before_enqueue, before_perform, etc.

GlobalID and Active Model GlobalID

Active Job supports GlobalID for parameters as well as Active Model GlobalID for lower versions. This makes it possible to pass live Active Record objects to your job instead of class/id pairs, which you then have to deserialize manually.

Before:


class TrashableCleanupJob < ActiveJob::Base
  def perform(trashable_class, trashable_id, depth)
    trashable = trashable_class.constantize.find(trashable_id)
    trashable.cleanup(depth)
  end
end

After:


class TrashableCleanupJob < ActiveJob::Base
  def perform(trashable, depth)
    trashable.cleanup(depth)
  end
end

Conclusion

As you saw here, Active Job is pretty neat by allowing queueing jobs in a lot of queueing backends such as Sidekiq, Delayed Job and so on. This is very convenience because is built on Rails 4.2 (or a gem for lower versions), and it’s pretty easy to use. It also supports GlobalID, so we can pass live Active Record objects without having to deserialize them manually.

For more information, I recommend you to go to Active Job Basics to learn more. And that’s it. Thanks for reading and see you in the next one!

Beginner
Administrate review
Events
RailsConf 2017 Adventure
Best Practices
De Código, Café y Cervezas 07 – ¿Somos profesionales?