我最近开始使用 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's Sidekiq::ScheduledSet.new
,我不会得到当前的工作,因为它已经从 Redis 中撤出。
编辑 2:
对于任何感兴趣的人,我采用了类似但不同的方法。基本上,我没有比较 的开始时间,而是Task
在模型和 Sidekiq 调用中添加了一个额外的字段,称为start_token
. 如果使用与对象相同的令牌调用了 sidekiq 作业,则它是有效的,否则丢弃并跳过该作业。每次模型更改 start_time 时,令牌都会更新。