我有一个每 4 分钟运行一次的 Sidekiq 作业。
此作业在再次执行代码之前检查当前代码块是否正在执行
process = ProcessTime.where("name = 'ad_queue_process'").first
# Return if job is running
return if process.is_running == true
如果 Sidekiq 在代码块中途重启,更新作业状态的代码将永远不会运行
# Done running, update the process times and allow it to be ran again
process.update_attributes(is_running: false, last_execution_time: Time.now)
这导致作业永远不会运行,除非我运行更新语句来设置 is_running = false
有什么办法可以在Sidekiq重启之前执行代码吗?
最佳答案
更新:
- 感谢@Aaron,并根据我们的讨论(下面的评论),
ensure
block (由 fork 的工作线程执行)只能在主线程之前运行几毫秒-thread 强行终止这些工作线程,以便主线程对异常堆栈进行一些“清理”,以避免被 Heroku 发出 SIGKILL。因此,请确保您的ensure
代码应该非常快!
长话短说:
def perform(*args)
# your code here
ensure
process.update_attributes(is_running: false, last_execution_time: Time.now)
end
无论方法“成功”还是引发异常,上面的
ensure
总是被调用。我测试了这个:看这个 repl code , 然后点击“运行”换句话说,即使在
SignalException
上,即使信号是SIGTERM
(正常关闭信号),也总是会调用它,但 ONLY EXCEPT 在SIGKILL
上(强制无法挽救的关机)。您可以通过检查我的 repl code 来验证此行为,然后将Process.kill('TERM', Process.pid)
修改为Process.kill('KILL', Process.pid)
,然后点击“运行”再次(你会注意到puts
不会被调用)查看 Heroku docs ,我引用:
When Heroku is going to shut down a dyno (for a restart or a new deploy, etc.), it first sends a SIGTERM signal to the processes in the dyno.
After Heroku sends SIGTERM to your application, it will wait a few seconds and then send SIGKILL to force it to shut down, even if it has not finished cleaning up. In this example, the ensure block does not get called at all, the program simply exits
... 这意味着将调用
ensure
block ,因为它是SIGTERM
而不是SIGKILL
,除非关闭down 需要很长时间,这可能是由于(我能想到 ATM 的一些原因):perform
代码(或堆栈中的任何 ruby 代码;甚至是 gems)中的某些东西也挽救了SignalException
,甚至挽救了根Exception
类,因为SignalException
是Exception
的子类)但需要很长时间清理(即清理connections
到 DB或者挂起你的应用程序的东西,或者 I/O 东西)或者,您自己的
ensure
block 需要很长时间。 I.E 在执行process.update_attributes(...)
时,由于某种原因 DB 临时挂起/网络延迟或超时,那么update
可能根本不会成功!并且会用完时间,其中来 self 上面的引述,在SIGTERM
几秒后,应用程序将被 Heroku 发送SIGKILL
强制停止。
... 这意味着我的解决方案仍然不完全可靠,但在正常情况下应该可以工作
关于ruby-on-rails - 有没有办法在 Sidekiq 在工作中重新启动之前运行代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54538852/