Планирование Rufus-планировщика дважды, несмотря на использование блокировок - PullRequest
0 голосов
/ 05 октября 2018

Я хорошо понимаю, почему это происходит (две среды исполнения ruby) и что это обычная проблема для людей, которые раньше не читали FAQ по RS или искали его в SO, но я потратил пару дней, пытаясьмногие предписанные решения все же мой rufus-планировщик продолжает вызывать дважды.

Это происходит только на производстве, на 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($0).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: false и blocking: true, новые экземпляры MyOtherSidekiqWorker будут по-прежнему вызываться, пока он еще работает.

Я, должно быть, упускаю что-то очевидное, спасибо за вашу помощь.

1 Ответ

0 голосов
/ 05 октября 2018

Итак, Heroku dynos не разделяет файловую систему

.rufus-scheduler-a.lock, видимый на dyno d0, не .rufus-scheduler-a.lock, видимый на dyno d1.

ВашДинозавры Heroku не используют одну и ту же файловую систему, а также они не используют один и тот же процесс Ruby и, следовательно, не один и тот же экземпляр rufus-scheduler.Так что overlap: false, blocking: true не будет иметь никакого эффекта от dyno d0 до dyno d1.

Вы можете реализовать собственный механизм блокировки для rufus-планировщика, получая вдохновение от https://github.com/jmettraux/rufus-scheduler#advanced-lock-schemes (возможно, черезбазы данных, потому что она используется вашими процессами Ruby), но это не поможет с overlap: false и blocking: true.

Если вы все еще хотите иметь overlap: false и blocking: true, вы можете посмотреть на https://devcenter.heroku.com/articles/scheduled-jobs-custom-clock-processes и планирование происходит в выделенном процессе / dyno с помощью rufus-scheduler или Clockwork и не нуждается в блокировке расписания.

Остальная часть моего ответа о вашем коде, а не о двойном планированиивы испытываете.

scheduler.down?

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

Почему у вас это unless b_scheduler.down?, если b_scheduler не работает, блок не будет выполненвообще.

Этого достаточно:

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

a_scheduler vs b_scheduler

Вам не нужен один планировщик для каждой работы.Вы можете просто написать:

require 'rufus-scheduler'                                                   

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

unless defined?(Rails::Console) || File.split($0).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
...