尽管使用锁,Rufus-scheduler 还是调度了两次

Rufus-scheduler scheduling twice despite using locks

我很清楚为什么会发生这种情况(两个 ruby 运行时),对于那些以前没有阅读 RS FAQ 或搜索过 SO 的人来说,这是一个常见问题,但我'我花了几天时间尝试了许多规定的解决方案,但我的 rufus-scheduler 继续调用两次。

这仅发生在生产中,运行 Rails 5.0.6,Puma 服务器,在 Heroku 上。

这是我的 scheduler.rb:

require 'rufus-scheduler'

a_scheduler = Rufus::Scheduler.new(:lockfile => ".rufus-scheduler-a.lock")
b_scheduler = Rufus::Scheduler.new(:lockfile => ".rufus-scheduler-b.lock")

unless defined?(Rails::Console) || File.split([=10=]).last == 'rake' || !Rails.env.production?
  a_scheduler.cron '0 21 * * *', overlap: false, blocking: true do
    MySidekiqWorker.perform_async unless a_scheduler.down? 
  end

  b_scheduler.every '1h', overlap: false, blocking: true do
    MyOtherSidekiqWorker.perform_async unless b_scheduler.down?
  end
end

我试过锁定文件,配置我自己的 scheduler_lock,.every.cron 的不同参数。此外,似乎即使我有 overlap: falseblocking: trueMyOtherSidekiqWorker 的新实例仍将被调用,而一个仍然是 运行。

我肯定遗漏了一些明显的东西,谢谢你的帮助。

所以,Heroku dynos not sharing the file system

在 dyno d0 上看到的 .rufus-scheduler-a.lock 不是在 dyno d1 上看到的 .rufus-scheduler-a.lock

你的 Heroku dynos 不共享相同的文件系统,它们也不共享相同的 Ruby 进程,因此不是相同的 rufus-scheduler 实例。所以overlap: false,blocking: true不会对dyno d0到dyno d1有任何影响。

您可以为 rufus-scheduler 实现自定义锁定机制,从 https://github.com/jmettraux/rufus-scheduler#advanced-lock-schemes 中汲取灵感(可能是通过数据库,因为它由您的 Ruby 进程共享),但这对 [=15 没有帮助=] 和 blocking: true.

如果您仍然想要 overlap: falseblocking: true,您可以查看 https://devcenter.heroku.com/articles/scheduled-jobs-custom-clock-processes 并使用 rufus-scheduler 或在专用的 process/dyno 中进行调度发条,不需要时间表锁定。

我的其余回答是关于您的代码,而不是关于您正在经历的双重调度。

scheduler.down?

b_scheduler.every '1h', overlap: false, blocking: true do
  MyOtherSidekiqWorker.perform_async unless b_scheduler.down?
end

如果 b_scheduler 关闭,为什么会有这个 unless b_scheduler.down? 块根本不会被执行。

这就足够了:

b_scheduler.every '1h', overlap: false, blocking: true do
  MyOtherSidekiqWorker.perform_async
end

a_scheduler 对比 b_scheduler

您不需要为每项作业配备一个调度程序。你可以简单地写:

require 'rufus-scheduler'                                                   

#scheduler = Rufus::Scheduler.new(lockfile: '.rufus-scheduler.lock')     
scheduler = Rufus::Scheduler.new                                  

unless defined?(Rails::Console) || File.split([=12=]).last == 'rake' || !Rails.env.production?                                                                  
  scheduler.cron '0 21 * * *', overlap: false, blocking: true do            
    MySidekiqWorker.perform_async                                           
  end                                                                       
  scheduler.every '1h', overlap: false, blocking: true do                   
    MyOtherSidekiqWorker.perform_async                                      
  end                                                                       
end