我有一个Sidekiq作业,每4分钟运行一次。
此作业会在再次执行代码之前检查当前代码块是否正在执行
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之前有什么方法可以执行代码?
答案 0 :(得分:3)
ensure
块(由分支的工作线程执行)只能在主线程被强制运行之前的无保证毫秒内运行终止这些工作线程,以便主线程对异常堆栈进行一些“清理”,以避免被Heroku SIGKILL编辑。因此,请确保您的ensure
代码应该非常快!TL; DR:
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,我引用:
当Heroku打算关闭dyno(用于重新启动或新部署等)时,它首先向dyno中的进程发送SIGTERM信号。
Heroku将SIGTERM发送到您的应用程序后,它将等待几秒钟,然后发送SIGKILL强制关闭它,即使它尚未完成清理。在此示例中,完全不调用sure块,程序仅退出
... ...这意味着将调用ensure
块,因为它是SIGTERM
而不是SIGKILL
,除非关闭需要很长的时间,否则可能会到(我可能会想到ATM的某些原因):
您的perform
代码(或堆栈中的任何ruby代码;甚至是宝石)中的某些内容也拯救了SignalException
,甚至拯救了根Exception
类,因为{ {1}}是SignalException
的子类,但需要花费很长时间进行清理(即清理Exception
到数据库或某些东西,或将挂起您的应用程序的I / O东西)
或者,您自己的connections
块需要很长的时间。即,当执行ensure
时,由于某种原因,数据库临时挂起/网络延迟或超时,那么process.update_attributes(...)
可能根本无法成功!并用完了时间(根据我上面的引用),在update
之后几秒钟后,Heroku发送SIGTERM
来强制停止应用程序。
...这都意味着我的解决方案仍不完全可靠,但在正常情况下应该可以使用
答案 1 :(得分:0)
处理sidekiq关闭异常
class SomeWorker
include Sidekiq::Worker
sidekiq_options queue: :default
def perform(params)
...
rescue Sidekiq::Shutdown
SomeWorker.perform_async(params)
end
end