【问题标题】:Rails: Cancelling a scheduled job in SidekiqRails:取消 Sidekiq 中的计划作业
【发布时间】:2014-08-02 13:43:13
【问题描述】:

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

【问题讨论】:

    标签: ruby-on-rails sidekiq


    【解决方案1】:

    API 文档概述了您可以做什么,但您确实需要深入了解源代码以发现所有功能。

    您可以这样做,但效率不高。这是一个线性扫描,用于按 JID 查找计划作业。

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

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

    【讨论】:

    • 有一个错字:ScehduledSet -> ScheduledSet
    • 这就是这样做的方法。只需让它检查它在运行时是否仍然相关。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 在您的情况下,您可以在计划作业时传入时间戳,然后在运行作业之前检查数据库以确保时间戳相同。
      • 小心,将时间戳作为参数传递给工人;在比较之前我必须Time#round他们。 Sidekiq doc 表示: >传递给 perform_async 的参数必须由简单的 JSON 数据类型组成:字符串、整数、浮点数、布尔值、空值、数组和哈希。 [...更多解释缩短了评论...]。 不要传递符号、命名参数或复杂的 Ruby 对象(如日期或时间!),因为它们无法正确地在转储/加载往返过程中存活。
      • 我想当我想到时间戳时,我想到的是自纪元以来的毫秒。
      猜你喜欢
      • 1970-01-01
      • 2021-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-08
      相关资源
      最近更新 更多