Rails 7 ActiveJob adds the ability to communicate enqueue failures to callers


ActiveJob is used to enqueue and run the tasks in the background. But a job can fail because of many reasons. Say a network issue while communicating to the underlying datastore or non-availability of the datastore, etc.

ActiveJob has added the ability to communicate the failures related to enqueuing the job to the caller. This will allow developers to have a better grasp of the flows related to the job enqueue.

Before

While calling perform_later, there was no clean way for the caller to understand the reason behind job enqueue failures. If the enqueuing succeeded, the method would return the job object. But if the job could not be enqueued, it would just return false and one would have to search the logs or implement something peculiar to find out what’s wrong.

After the change

The commit will allow perform_later to optionally accept a block. Irrespective of the enqueuing status, the block will receive a job instance as an argument. This is achieved by yielding given block after the enqueue method is executed but before the result is returned.

This will be helpful not only in failure scenarios but will also help in those rare scenarios where one wants to do something right after a successful enqueue.

Heres an example of the perform_later method with a block,

SendEmailVerificationJob.perform_later do |job|
  if job.successfully_enqueued?
    # do something - log the successful enqueue
  else
    if job.enqueue_error&.message == "Redis was unavailable"
      # invoke some code that will retry the job after a delay
    end
  end
end

We can see the safe navigation at job.enqueue_error&.message. That’s because the enqueue_error is assigned to the job instance only when the underlying adapter raises an EnqueueError. It’s a new exception class added which can be used by the adapters (eg: Sidekiq, DelayedJob) for enqueue-related errors.

To summarize, the job instance will have two new properties successfully_enqueued and enqueue_error. Out of which enqueue_error will be populated only when an EnqueueError is rescued.