M. *_*ord 14 ruby ruby-on-rails heroku delayed-job
重新启动Heroku工作程序时(无论是在命令上还是作为部署的结果),Heroku都会SIGTERM向工作进程发送.在这种情况下delayed_job,SIGTERM 信号被捕获,然后工作人员在当前作业(如果有)停止后停止执行.
如果工人需要很长时间才能完成,那么Heroku将会发送SIGKILL.在这种情况下delayed_job,这会在数据库中留下一个锁定的作业,不会被另一个工作人员接收.
我想确保工作最终完成(除非出现错误).鉴于此,最好的方法是什么?
我看到两个选择.但我想获得其他意见:
delayed_job以在收到时停止处理当前作业(并释放锁定)SIGTERM.有什么想法吗?
Ale*_*eth 28
现在,一个更好的解决方案内置于delayed_job中.使用此设置通过在初始化程序中添加此项来在TERM信号上引发异常:
Delayed::Worker.raise_signal_exceptions = :term
Run Code Online (Sandbox Code Playgroud)
使用该设置,作业将在heroku发出针对非合作进程的最终KILL信号之前正确清理并退出:
您可能需要在SIGTERM信号上引发异常,Delayed :: Worker.raise_signal_exceptions =:term将导致worker引发SignalException,导致正在运行的作业中止并被解锁,这使得该作业可供其他工作人员使用.此选项的默认值为false.
可能的值raise_signal_exceptions是:
false- 不会引发异常(默认):term - 只会在TERM信号上引发异常,但INT将等待当前作业完成.true - 将在TERM和INT上引发异常从3.0.5版开始提供.
请参阅:https://github.com/collectiveidea/delayed_job/commit/90579c3047099b6a58595d4025ab0f4b7f0aa67a
duk*_*ave 12
TLDR:
把它放在你的工作方法的顶部:
begin
term_now = false
old_term_handler = trap 'TERM' do
term_now = true
old_term_handler.call
end
Run Code Online (Sandbox Code Playgroud)
和
确保每十秒至少调用一次:
if term_now
puts 'told to terminate'
return true
end
Run Code Online (Sandbox Code Playgroud)
和
在你的方法结束时,把这个:
ensure
trap 'TERM', old_term_handler
end
Run Code Online (Sandbox Code Playgroud)
说明:
我遇到了同样的问题并且发现了这篇Heroku文章.
这个工作包含一个外循环,所以我按照文章添加了一个trap('TERM')和exit.然而,delayed_job 选择那个failed with SystemExit并将任务标记为失败.
随着SIGTERM现在我们被困trap 工人的处理程序不被调用,而是立即重新启动作业,然后得到SIGKILL一个几秒钟后.回到原点.
我尝试了一些替代方案exit:
A return true将作业标记为成功(并将其从队列中删除),但如果队列中有另一个作业正在等待,则会遇到同样的问题.
调用exit!将成功退出作业和工作程序,但它不允许工作程序从队列中删除作业,因此您仍然有"孤立锁定作业"问题.
我的最终解决方案是我在答案的顶部给出的解决方案,它由三部分组成:
在我们开始潜在的长作业之前,我们'TERM'通过执行一个新的中断处理程序trap(如Heroku文章中所述),我们使用它来设置term_now = true.
但是,我们也必须抢old_term_handler在这延迟在职职工代码集(由返回trap),并记住call它.
我们仍然必须确保我们将控制权返回到Delayed:Job:Worker有足够的时间进行清理和关闭,因此我们应该term_now至少每隔十秒检查一次(return如果是)true.
您可以return true或return false根据您是否希望将工作视为成功.
最后,记住删除处理程序并在完成后安装回处理程序至关重要Delayed:Job:Worker.如果你没有这样做,你将保留对我们添加的那个的悬空引用,如果你在其上添加另一个(例如,当工人再次启动这个工作时),可能会导致内存泄漏.