【发布时间】:2013-04-08 13:57:34
【问题描述】:
我最近开始使用 Sidekiq,并注意到它有一个我一直在寻找的很棒的功能:
UserMailer.delay_until(5.days.from_now).find_more_friends_email
基本上我可以安排未来的工作,因此我不需要我的应用不断轮询具有开始时间的新事件。
现在这就像一个魅力,但我如何更改工作的开始时间?通常,一些预定事件的开始时间会发生变化。我如何在 sidekiq 中复制它?
我知道我可以删除作业并创建一个新作业,但是否可以只修改开始时间?
编辑:
我建立在 Oto Brglez 的想法之上,这里是文档化的代码:
module TaskStuff
class TaskSetter
include Sidekiq::Worker
sidekiq_options retry: false
def perform(task_id)
task = Task.find(task_id)
# Get the worker that's performing this job
if worker = AppHelpers.find_worker(jid)
# If the worker matches the "at" timestamp then this is the right one and we should execute it
if worker.last["payload"]["at"].to_s.match(/(\d*\.\d{0,3})\d*/)[1] == task.start.to_f.to_s.match(/(\d*\.\d{0,3})\d*/)[1]
task.execute
else
custom_logger.debug("This worker is the wrong one. Skipping...")
end
else
custom_logger.error("We couldn't find the worker")
end
end
end
end
module AppHelpers
[...]
def self.find_worker(jid)
Sidekiq::Workers.new.select {|e| e.last["payload"]["jid"] == jid}.first
end
[...]
end
> task = Task.create(start: 5.hours.from_now)
> TaskStuff::TastSetter.perform_at(task.start, task.id)
现在如果我这样做
> task.update_attributes(start: 4.hours.from_now)
> TaskStuff::TastSetter.perform_at(task.start, task.id)
该任务将在 4 小时内执行,而另一个作业(将在 5 小时内执行)将在到达时间时被忽略并删除。
最初我尝试使用Time.now 而不是worker.last["payload"]["at"],但这可能非常不准确,因为计划的作业不会总是按时执行。检查间隔为 15 秒,如果所有工作人员都在其他地方忙,则作业可能会进一步延迟。
我必须使用Regexp 来匹配开始时间,因为在读取task.start 时,我可能会得到一个小数位数不同的浮点数,并且 if 条件不会通过。这样,我将两个值都设为小数点后 3 位。
我发现获取作业的“at”属性的唯一方法是通过工作人员获取它。如果我要问 Redis 或使用 Mike 的 Sidekiq::ScheduledSet.new,我不会得到当前的工作,因为它已经从 Redis 中撤出。
编辑 2:
对于任何感兴趣的人,我采用了类似但不同的方法。基本上,我没有比较 Task 的开始时间,而是在模型和 Sidekiq 调用中添加了一个额外的字段,称为 start_token。如果使用与对象相同的令牌调用了 sidekiq 作业,则它是有效的,否则丢弃并跳过该作业。每次模型更改 start_time 时都会更新令牌。
【问题讨论】:
-
根据您的第二次编辑,这可能会有所帮助,因为我在工作中使用 ActiveRecord 对象,因此我使用的是 updated_at 字段。我用我的 ActiveRecord id 将它传递给我的工作,当我的工作开始时,我检查 updated_at 是否仍然相同
标签: ruby-on-rails ruby sidekiq