Rails: 在Sidekiq中取消一个预定的任务

8

我有一个在模型中的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?
3个回答

24

API文档提供了您可以执行的概述,但您需要深入源代码以发现所有功能。

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

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

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


有一个打字错误:ScehduledSet -> ScheduledSet - DickieBoy
这是正确的方法。只需在运行时检查它是否仍然相关即可。 - Doughtz

1
你写的伪代码应该可以运行,不过我会删除if/else块。如果未找到该项,则Sidekiq::Status.cancel将简单地返回false。因此,以下伪代码应该没问题:
1) 如果scheduled_job_id存在,则取消scheduled_job_id。
2) 运行NotifierWorker.perform_at ... - 无论您是否取消都会执行。
然而,我要注意,正如@mike-perham所说,它会很慢(线性搜索)。因此,当我实现Sidekiq::Status.cancel时,我添加了一个可选的第二个参数来表示时间戳。如果传递时间戳,则Redis将使用二进制搜索查找匹配该时间的计划任务,因此它只需在线性搜索中搜索在完全相同时间安排的项目之间。
因此,在取消时,您应该运行: Sidekiq::Status.cancel(self.scheduled_job_id, self.release_time_was)

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

在您的情况下,您可以在调度作业时传递时间戳,然后检查数据库以确保时间戳相同才运行作业。 - Jared Menard
注意,传递时间戳作为参数给worker时要小心;在比较之前,我必须使用Time#round对它们进行舍入。 Sidekiq doc说:
您传递给perform_async的参数必须由简单的JSON数据类型组成:字符串,整数,浮点数,布尔值,null,数组和哈希。 [...更多解释缩短为评论...]. 不要传递符号,命名参数或复杂的Ruby对象(例如日期或时间!),因为这些都不会正确地经过转储/加载往返。
- pimpin
我想当我在考虑时间戳时,我是以自纪元以来的毫秒为基准思考的。 - Jared Menard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接