什么可能导致预定的 Rails 活动作业消失?

Posted

技术标签:

【中文标题】什么可能导致预定的 Rails 活动作业消失?【英文标题】:What might cause scheduled Rails Active Jobs to disappear? 【发布时间】:2017-06-19 19:14:45 【问题描述】:

我怀疑我们的一些活跃工作正在消失,但我不知道为什么。下面是我发现它消失的证据,但不是原因。

我们的网站使用外部云打印服务。我们开始工作,然后检查它们的状态。成功创建远程云打印后,我们创建一个活动作业以立即检查状态。如果它完成(成功或其他),它被标记为这样。如果不是,则检查状态作业会创建另一个作业,但会稍有延迟。延迟每次都会增加。

今天做了一次状态检查,日志显示等待时间达到了 128 秒。但是没有进行下一次状态检查,日志也没有错误。

我们使用由延迟作业支持的活动作业。状态检查作业的代码如下。它看不到逻辑中的任何缺陷,这些缺陷不会导致正确收集状态检查或再次尝试等待。

class CheckCloudPrintStatusJob < ApplicationJob
  queue_as :default

  def perform(cloud_print, count = 0)
    cloud_print.update_status

    unless cloud_print.finished?
      count += 1
      wait = 2**(count-1)

      if count > 15
        cloud_print.mark_as_failed

        puts "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
        puts "~~~~~~~~~~~~~~~~~~ Cloud printing ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
        puts "Cloud print ##cloud_print.id failed"
        puts "Finally waited #wait seconds and then cancelled."
        puts "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
      else
        puts "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
        puts "~~~~~~~~~~~~~~~~~~ Cloud printing ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
        puts "Checking status of cloud print ##cloud_print.id"
        puts "Waiting #wait seconds and then retrying."
        puts "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"

        CheckCloudPrintStatusJob.set(wait: wait.seconds).perform_later(cloud_print, count)
      end
    end
  end
end

【问题讨论】:

update_status 引发错误时会发生什么?你也可以在update_status分享代码吗? 【参考方案1】:

正确,所述逻辑中没有任何缺陷会导致正确收集状态检查或再次尝试等待。

我已通过以下设置验证了您的作业代码在超过 128 秒的等待后成功运行:

rails new项目 delayed_job_active_record 添加到 Gemfile(运行 bundle installrails generate delayed_job:active_recordrake db:migrate 安装 gems 并创建延迟作业数据库表 config.active_job.queue_adapter = :delayed_jobconfig/application.rb 一个基本的CloudPrint &lt; ApplicationRecord 模型,在app/models/cloud_print.rb 中带有update_statusfinished?mark_as_failed 方法 app/jobs/check_cloud_print_status_job.rb中提供的代码 通过 Rails 控制台运行 CheckCloudPrintStatusJob.perform_later(CloudPrint.create) 将作业排队 (bin/rails c)

由于上述序列正常运行而没有任何问题,因此您需要通过提供更多实际重现问题的complete and verifiable example 来扩大搜索范围。一旦您能够一致地重现您的问题,或者将您的整个 Rails 项目上传到 GitHub 存储库,或者调查您的环境和项目配置的其他方面。以下是一些可能性:

您的模型类中可能存在可能引发任何异常的逻辑; 工作进程守护进程可能已被中止或终止; 作业队列可能已被清除(例如,通过 rake jobs:clear) 另一个进程可能已经修改和/或删除了正在处理的模型对象; finished? 可能在调用update_status 后返回true,导致即使处理成功完成,最终状态检查也未打印。

注意- 延迟作业支持以5 seconds + N ** 4 的延迟重试失败的作业,其中N 是尝试次数,无需自己重新实现此逻辑。如果cloud_print.finished? 为假,则只需raise 一个异常,并且您不应该需要任何其他自定义延迟代码:

class CheckCloudPrintStatusJob < ApplicationJob
  queue_as :default

  def perform(cloud_print)
    raise 'Not ready' unless cloud_print.finished?
  end
end

【讨论】:

感谢您的全面响应。虽然您的回答并没有直接为我们解决问题,但它有助于让我们思考问题可能出在哪里。奇怪的是,解决方案似乎是(除了增加很多额外的安全性)从 dj 切换到 sidekiq。显然这不应该有什么不同,但显然它确实有。【参考方案2】:

正如作业代码所暗示的那样,参数cloud_print 是某个Ruby 类的实例(似乎是ActiveRecord::Base)。一般来说,将复杂的对象作为后台作业的参数并不是一个好主意,因为这些参数必须序列化为字符串、json 或 yaml。 DelayedJob 使用 YAML 序列化对象,有时可能无法恢复模型实例。例如,如果延迟作业作为回调 before_create 运行 - 模型对象尚未保存且无法恢复。更多信息可以在这里找到https://github.com/collectiveidea/delayed_job/wiki/Common-problems#jobs-are-silently-removed-from-the-database

【讨论】:

从 Rails 5 开始(至少),ActiveJob 在传递 ActiveModel 时使用 GlobalId,如此处所述 guides.rubyonrails.org/active_job_basics.html#globalid 这意味着我们可以传递一个 ActiceRecord 对象,它将存储 Class + Id 而不会序列化对象的属性。我们仍然需要处理事务的 after_create 问题

以上是关于什么可能导致预定的 Rails 活动作业消失?的主要内容,如果未能解决你的问题,请参考以下文章

Rails:放置在哪里以及如何调用预定的清理方法

预定的未来会导致内存泄漏吗?

如何在播放框架中获得预定作业的结果?

我可以安排一个包通过 Oracle 预定的作业运行吗

如何在启动后的预定时间打开活动

PHP Wordpress中的预定活动