ruby-on-railsrubydelayed-jobrails-activejob

How do I create delayed_job jobs with hooks/callbacks?


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?


Solution

  • 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.