How do I create delayed_job jobs with hooks/callbacks?

Viewed 871

I am using the most basic version of delayed_job in a Rails app. I have the max time allowed for a delayed_job set at 10 minutes. I would like to get the hooks/callbacks working so I can do something after a job stop executing at the 10 minute mark.

I have this set in my rails app: config.active_job.queue_adapter = :delayed_job

This is how I normally queue a job: object.delay.object_action

The hook/callback example is for a named job but the basic, getting started steps are not for a named job. So I don't think I have a named job. Here is the example given to get the callbacks working:

class ParanoidNewsletterJob < NewsletterJob
  def enqueue(job)
    record_stat 'newsletter_job/enqueue'
  end

  def perform
    emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) }
  end

  def before(job)
    record_stat 'newsletter_job/start'
  end

  def after(job)
    record_stat 'newsletter_job/after'
  end

  def success(job)
    record_stat 'newsletter_job/success'
  end

  def error(job, exception)
    Airbrake.notify(exception)
  end

  def failure(job)
    page_sysadmin_in_the_middle_of_the_night
  end
end

I would love to get the after or error hooks/callbacks to fire.

Where do I put these callbacks in my Rails app to have them fire for the basic delayed_job setup? If I should be using ActiveJob callbacks where do you put those callbacks given delayed_job is being used?

2 Answers

You cannot use object.delay.object_action convenience syntax if you want more advanced features like callbacks. The #delay convenience method will generate a job object that works similar to this:

# something like this is already defined in delayed_job
class MethodCallerJob
  def initialize(object, method, *args)
    @object = object
    @method = method
    @args = args
  end

  def perform
    @object.send(@method, *@args)
  end
end

# `object.delay.object_action` does the below automatically for you

# instantiates a job with your object and method call
job = MethodCallerJob.new(object, :object_action, [])
Delayed::Job.enqueue(job) # enqueues it for running later

then later, in the job worker, something like the below happens:

job = Delayed::Job.find(job_id) # whatever the id turned out to be
job.invoke_job # does all the things, including calling #perform and run any hooks
job.delete # if it was successful

You have to create what the delayed_job README calls "Custom Jobs", which are just plain POROs that have #perform defined at a minimum. Then you can customize it and add all the extra methods that delayed_job uses for extra features like max_run_time, queue_name, and the ones you want to use: callbacks & hooks.

Sidenote: The above info is for using delayed_job directly. All of the above is possible using ActiveJob as well. You just have to do it the ActiveJob way by reading the documentation & guides on how, just as I've linked you to the delayed_job README, above.

You can create delayed_job hooks/callback by something like this

module Delayed
  module Plugins
   class TestHooks < Delayed::Plugin
     callbacks do |lifecycle|
       lifecycle.before(:perform) do |_worker, job|
         .....
       end
     end
   end
  end
end

And need this plugin to initializer

config/initializers/delayed_job.rb

require_relative 'path_to_test_plugin'
Delayed::Worker.plugins << Delayed::Plugins::TestHooks

Similar to perform there are also hooks for success failure and error.

And similar to 'before' you can also capture the 'after' hooks.

Related