So I have a Sidekiq worker in my model which looks like this:
class Perk < ActiveRecord::Base
include Sidekiq::Worker
include Sidekiq::Status::Worker
after_save :update_release_time
def update_release_time
if self.release_time_changed?
#if scheduled job already exists then cancel and reschedule
# Sidekiq::Status.cancel scheduled_job_id
# scheduled_job_id = NotifierWorker.perform_at(time.seconds.from_now, .....)
#elsif scheduled job doesn't exist, then schedule for the first time
# scheduled_job_id = NotifierWorker.perform_at(time.seconds.from_now, .....)
#end
end
end
end
So basically, my code checks if the release time has changed. If it has, then it has to cancel the previously scheduled job and schedule it at a new time. How do I achieve this ie what would go in place of my pseudo-code? How do I check if scheduled_job_id
exists and then fetch its id?
The API documentation has an overview of what you can do but you really need to dive into the source to discover all the capabilities.
You can do this but it won't be efficient. It's a linear scan for find a scheduled job by JID.
require 'sidekiq/api'
Sidekiq::ScheduledSet.new.find_job(jid).try(:delete)
Alternatively your job can look to see if it's still relevant when it runs.
The pseudocode that you wrote should work, though I'd remove the if/else block. Sidekiq::Status.cancel
will simply return false if the item was not found. So the following pseudocode should be fine:
1) Cancel scheduled_job_id if scheduled_job_id.present?
2) Run NotifierWorker.perform_at ...
- this is done regardless of whether you cancel or not anyway.
However, I will note, as @mike-perham stated, it will be slow (a linear search). As such, when I implemented Sidekiq::Status.cancel
I added an optional second parameter for the timestamp. If you pass a timestamp then Redis will find scheduled tasks matching that time using binary search, so it only has to search linearly among the items scheduled at the exact same time.
As such, when cancelling you should run:
Sidekiq::Status.cancel(self.scheduled_job_id, self.release_time_was)
Use a UUID saved in a DB or cache to make sure that you still need to run a job.
class SmartWorker
include Sidekiq::Worker
sidekiq_options :queue => :low,
:retry => false,
:backtrace => false
def self.schedule id
uuid = SecureRandom.uuid
Redis.new.set("#{id}-key", uuid)
SmartWorker.perform_in(1.minute, id, uuid)
end
def perform(id, uuid, force=false)
return unless uuid == Redis.new.get("#{id}-key")
if force || CronLockService.lock("lock", 5000)
begin
Model.find(id).relation.find_each{|it|
Service.do_it(it)
}
ensure
CronLockService.expire("lock")
end
end
end
end
So if this happened, it would only run once
SmartWorker.schedule 1
SmartWorker.schedule 1
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.