所以我的模型中有一个Sidekiq工作人员,看起来像这样:
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
基本上,我的代码检查发布时间是否已经改变。如果有,则必须取消先前安排的作业并在新的时间安排它。我如何实现这一点,即代替我的伪代码?如何检查scheduled_job_id
是否存在然后获取其ID?
答案 0 :(得分:20)
API文档概述了您可以执行的操作,但您确实需要深入了解源代码以发现所有功能。
你可以这样做,但效率不高。这是JID找到预定作业的线性扫描。
require 'sidekiq/api'
Sidekiq::ScheduledSet.new.find_job(jid).try(:delete)
或者,您的工作可以查看它在运行时是否仍然相关。
答案 1 :(得分:1)
您编写的伪代码应该可以工作,但我会删除if / else块。如果找不到该项,Sidekiq::Status.cancel
将返回false。所以下面的伪代码应该没问题:
1)Cancel scheduled_job_id if scheduled_job_id.present?
2)运行NotifierWorker.perform_at ...
- 无论你是否取消,都会这样做。
但是,我会注意到,正如@ mike-perham所说,它会很慢(线性搜索)。因此,当我实现Sidekiq::Status.cancel
时,我为时间戳添加了可选的第二个参数。如果您传递时间戳,那么Redis将使用二进制搜索找到与该时间匹配的计划任务,因此它只需要在同一时间安排的项目之间进行线性搜索。
因此,取消时你应该运行:
Sidekiq::Status.cancel(self.scheduled_job_id, self.release_time_was)
答案 2 :(得分:1)
使用保存在数据库或缓存中的UUID来确保您仍然需要运行作业。
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
所以,如果发生这种情况,它只会运行一次
SmartWorker.schedule 1
SmartWorker.schedule 1