Rails:取消Sidekiq的预定作业

所以我的模型中有一个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?

API文档概述了您可以执行的操作,但您确实需要深入了解源代码以发现所有function。

你可以这样做,但效率不高。 这是一个线性扫描,用于通过JID查找预定作业。

 require 'sidekiq/api' Sidekiq::ScheduledSet.new.find_job(jid).try(:delete) 

或者,您的工作可以查看它在运行时是否仍然相关。

您编写的伪代码应该可以工作,但我会删除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)

使用保存在数据库或缓存中的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