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.
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!
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With