延迟任务:如何强制处理失败的任务

3
我正在维护一个Rails应用程序,其中正在使用delayed_job gem发送电子邮件。
我刚刚注意到,由于应用程序中的一个bug,所有延迟的作业在过去几天里都失败了。现在这个bug已经修复了,我想尽快处理这些作业,但它们已经尝试失败太多次了,工作者从数据库中提取它们的时间间隔非常长。
我已经尝试通过更新delayed_jobs表并将尝试次数设置为较小的数字,并将run_at属性设置为当前时间,但仍然没有帮助。
你能告诉我如何强制工作者执行它们吗?
4个回答

10

你可以手动启动它,尝试:

Delayed::Job.all.each { |j| j.invoke_job }
或者
Delayed::Job.all.each { |j| j.payload_object.perform }

听起来很合理,我现在会尝试一下 :) - Evgeniya Manolova
所以,我尝试了两种方法,但是工作仍然没有被处理。我是从Rails控制台执行的,你有什么想法我做错了什么吗? - Evgeniya Manolova

3

好的,最后我明白了!

关键是将run_at属性更新为当前时间,但对于应用程序来说,当前时间比数据库晚3个小时。

当我设置它为now()- interval '3小时'时,所有作业都被处理了。

编辑:

@rodzyn,我尝试了您的建议,但仍然无法使其工作:

[20] pry(main)> Delayed::Job.all.size
  Delayed::Backend::ActiveRecord::Job Load (0.6ms)  SELECT "delayed_jobs".* FROM "delayed_jobs" 
=> 1
[21] pry(main)> Delayed::Job.first.invoke_job
  Delayed::Backend::ActiveRecord::Job Load (0.5ms)  SELECT "delayed_jobs".* FROM "delayed_jobs" LIMIT 1
  Order Load (0.4ms)  SELECT "orders".* FROM "orders" WHERE "orders"."id" = $1 LIMIT 1  [["id", "328"]]
  User Load (0.5ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
=> nil
[22] pry(main)> Delayed::Job.all.size
  Delayed::Backend::ActiveRecord::Job Load (0.6ms)  SELECT "delayed_jobs".* FROM "delayed_jobs" 
=> 1
[23] pry(main)> 

6
很难确定这个答案实际上建议的解决方案是什么。对我来说,我只需要在循环中更新以下内容,这将导致失败的作业重新处理并在成功后删除: Delayed :: Job.where(“failed_at不为空”)。each do |dj| dj.run_at = Time.now; dj.last_error = nil; dj.failed_at = nil; dj.save! end - steakchaser
我同意@steakchaser的看法,这个答案没有提供足够的解决方案信息。虽然那是很久以前的事情了,我也不太记得当时的情况。我已经取消了勾选,并将问题保留为未接受答案。 - Evgeniya Manolova

3

现有回答都没有完全正确,所以我在这里补充一下。

关键是要让延迟的作业相信这些作业并没有失败,因此通过Rails数据库控制台实现:

-> rails db
development# update delayed_jobs set run_at = now() - interval '3 hours', attempts = 0, failed_at = null;
UPDATE 30
development# \q

-> rake jobs:workoff  # a good one to use, because it will return immediately if no jobs are found

无论是"failed_at"还是"attempts"字段都可以阻止作业运行。


1
现在错误已经修复,我想尽快处理这些工作,但它们已经有太多的失败尝试了,而且工作者从数据库中提取它们的延迟非常大。
这意味着,由于作业没有从表中删除,因此仍然有一些延迟作业的尝试。延迟作业的默认行为是在找到可用作业时从队列中读取5个作业。一种不需要太多代码更改的方法是在延迟配置中设置一个选项,该选项将从队列中选择更多作业并执行。您可以通过设置Delayed :: Worker.read_ahead来进行配置。
# config/initializers/delayed_job_config.rb
Delayed::Worker.destroy_failed_jobs = false
Delayed::Worker.read_ahead = 10

Delayed::Worker.destroy_failed_jobs 可以避免在最大尝试次数后删除作业,这也是一个可配置的项目。

延迟作业每5秒钟检查一次数据库中是否有可用的作业,并在5 + N ** 4秒后尝试特定的作业。因此,如果某个特定的作业已经失败了24次,则其轮到的时间将在331781秒后到来,即大约3天后。


谢谢您的建议。我已经尝试过了,但它并没有解决我的问题 - 我的目标是在下一次计划执行之前强制执行。 - Evgeniya Manolova

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接