Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Rails 4.2 check if a job is already enqueued

I'm using Rails 4.2 and delayed_job 4.0.6 as my ActiveJob backend.

I have a job that I would like to only allow in the queue once. The job in question takes over a minute to run. It is enqueued by a callback on a model. The callback will fire much more often than the job can be completed. The job needn't be queued more than once in the future.

Here's some pseudocode for what I'm trying to accomplish.

# app/jobs/refresh_account_cache_job.rb
class RefreshAccountCacheJob < ActiveJob::Base
  def before_enqueue
    skip if job_already_enqueued
  end

  def perform
    Account.write_to_cache
  end

  def job_already_enqueued
    # ?
  end
end

If an instance of the job is running when it is called again, it should still be enqueued for the future. I'm looking for a way to have the job enqueued for a future run a maximum of 1 times.

I assume that the answer will have to be specific to delayed_job, but if it can be generalized to ActiveJob that would be even better.

like image 452
rb- Avatar asked Oct 19 '22 17:10

rb-


2 Answers

This may not be the exact fit, but it should get you pointed in the right direction:

def self.up
  create_table :delayed_jobs, :force => true do |table|
  table.integer  :priority, :default => 0, :null => false
  table.integer  :attempts, :default => 0, :null => false
  table.text     :handler,                 :null => false
  table.text     :last_error
  table.datetime :run_at
  table.datetime :locked_at
  table.datetime :failed_at
  table.string   :locked_by
  table.string   :queue
  table.timestamps
end

So you could add a status column to that table, then run a query such as this to grab the job and check its status before doing anything else.

Delayed::Job.where(queue: '<YOUR QUEUE>').where(id: params[:id]).status

How would you set the status, you ask? Well, use the success hook in delayed jobs. It would look a bit like this:

def success(job)
  update_status('success')
end

private

def update_status(status)
  job = Job.find job_id
  job.status = status
  job.save!
end

Hope this helps!

like image 123
aspencer8111 Avatar answered Nov 01 '22 17:11

aspencer8111


I'm posting what I have ended up doing as an answer to get feedback on it. This is just a possible solution I'm testing.

In the Job I am checking the Delayed::Job list to see if the current handler is present. If it is I skip the job.

# queue_job.rb
class EnqueueJob < ActiveJob::Base
  queue_as :default

  def already_enqueued?
    Delayed::Job.all.any? do |job|
      job.handler.include?("EnqueueJobHandler")
    end
  end

  def perform
    unless already_enqueued?
      # do stuff
    end
  end
end

So far it's keeping the job from overrunning the queue. The downside is that I don't know that I'm keeping the cache as up-to-date as I want to.

like image 33
rb- Avatar answered Nov 01 '22 17:11

rb-