Rails 4.2 从活动作业中获取延迟的作业 ID

Posted

技术标签:

【中文标题】Rails 4.2 从活动作业中获取延迟的作业 ID【英文标题】:Rails 4.2 get delayed job id from active job 【发布时间】:2015-07-03 13:07:05 【问题描述】:

知道如何从 ActiveJob 排队中获取 Delayed::Job id 吗?当我排队工作时,我会返回一个带有@job_idActiveJob::Base 实例,但该工作ID 似乎是ActiveJob 内部的。到目前为止,我最好的猜测是查看最近创建的工作:

active_job_id = GenerateReportJob.perform_later(self.id).job_id
delayed_job = Delayed::Job.order(id: :desc).limit(5).detect do |job|
  YAML.load(job.handler).job_data['job_id'] == active_job_id
end

但这似乎是各种 hacky。 ActiveJob 没有从Delayed::Job 返回 ID 有点令人惊讶,特别是因为这是在作业入队时显式返回的内容。

== 编辑

看来我不是唯一一个 (https://github.com/rails/rails/issues/18821)

【问题讨论】:

啊,但是有些人可能会争辩说,需要获得工作 ID 也是“各种 hacky”!出于好奇,也因为它可能会为另一种方法打开大门,为什么需要工作 ID? 这是为了能够在它们在队列中运行之前取消它们。 【参考方案1】:

受 Beguene 的回答和 Rails 5 ActiveJob 代码的一些逆向工程的启发,我已将其与 Rails 4.2 一起使用

1) 在lib/active_job/queue_adapters/delayed_job_adapter.rbconfig/initializers/delayed_job.rb 中添加以下代码(两个位置都有效):

# file: lib/active_job/queue_adapters/delayed_job_adapter.rb
module ActiveJob
  module Core
    # ID optionally provided by adapter
    attr_accessor :provider_job_id
  end

  module QueueAdapters
    class DelayedJobAdapter
      class << self
        def enqueue(job) #:nodoc:
          delayed_job = Delayed::Job.enqueue(JobWrapper.new(job.serialize), queue: job.queue_name)
          job.provider_job_id = delayed_job.id
          delayed_job
        end

        def enqueue_at(job, timestamp) #:nodoc:
          delayed_job = Delayed::Job.enqueue(JobWrapper.new(job.serialize), queue: job.queue_name, run_at: Time.at(timestamp))
          job.provider_job_id = delayed_job.id
          delayed_job
        end
      end
      class JobWrapper #:nodoc:
        attr_accessor :job_data

        def initialize(job_data)
          @job_data = job_data
        end

        def perform
          Base.execute(job_data)
        end
      end
    end
  end
end

attr_accessor :provider_job_id 语句在 Rails 4.2 中是必需的,因为它用于 enqueue 方法并且在 4.2 中尚未定义。

那么我们可以像下面这样使用它:

2) 定义我们自己的 ActiveJob 类:

# file: app/jobs/my_job.rb
class MyJob < ActiveJob::Base
  queue_as :default

  def perform(object, performmethod = method(:method))
    # Do something later
      returnvalue = object.send(performmethod)
      returnvalue
    end

  end
end

3) 现在我们可以在代码的任意位置创建一个新作业:

job = MyJob.perform_later(Myobject, "mymethod")

这会将方法Myobject.mymethod 放入队列中。

4) 1) 中的代码帮助我们找到与我们的工作相关的延迟工作:

delayed_job = Delayed::Job.find(job.provider_job_id)

5) 最后,我们可以做任何我们需要对 delay_job 做的事情,例如删除它:

delayed_job.delete

注意:在 Rails 5 中,步骤 1) 将不再需要,因为完全相同的代码是 Rails 5 的组成部分。

【讨论】:

@kddeisz:我只测试了 Beguene 的答案(因为它更接近 Rails 5 代码,你的答案太聪明了,我无法理解;但它看起来很棒!)。在 Beguene 的回答中,只缺少一个微小的成分:attr_accessor :provider_job_id 声明。更好的选择是在 Beguene 的答案中添加评论,但是,由于我是 *** 的新手并且没有声誉,我没有添加 cmets 的权限。因此,我认为,用我需要的所有成分来回答这个问题是有意义的。 这很好,如果您想在enqueue 回调中获取提供者 ID(延迟作业 ID),但是,它不会在执行时给您 ID(在 @ 987654332@回调)【参考方案2】:

以防万一将来有人发现:Rails 刚刚接受了一个补丁,允许您从 Rails 5 中的 provider_job_id 获取此 id。您可以使用类似的补丁来使用它

ActiveJob::QueueAdapters::DelayedJobAdapter.singleton_class.prepend(Module.new do
  def enqueue(job)
    provider_job = super
    job.provider_job_id = provider_job.id
    provider_job
  end

  def enqueue_at(job, timestamp)
    provider_job = super
    job.provider_job_id = provider_job.id
    provider_job
  end
end)

【讨论】:

感谢您发布答案!但是,将这段代码放在哪里呢? @Alexander 通常这将进入初始化程序。类似 config/initializers/delayed_job.rb 在那个补丁到来之前,我遇到了这个问题,并做了一个 gem 来通过 ActiveJob 查询作业状态:github.com/cdale77/active_job_status 除此之外,您可能还需要将attr_accessor :provider_job_id 添加到 ApplicationJob(如果您正在这样做),或者添加到您的每个 Job 类。将@kddeisz 的代码添加到初始化程序中效果非常好。 如果对任何人来说仍然不够清楚,在 Rails 5 中你可以说@job.provider_job_id【参考方案3】:

我使用 Rails 5 的新补丁使其在 Rails 4.2 中工作,如下所示:

创建文件lib/active_job/queue_adapters/delayed_job_adapter.rb

module ActiveJob
  module QueueAdapters
    class DelayedJobAdapter
      class << self
        def enqueue(job) #:nodoc:
          delayed_job = Delayed::Job.enqueue(JobWrapper.new(job.serialize), queue: job.queue_name)
          job.provider_job_id = delayed_job.id
          delayed_job
        end

        def enqueue_at(job, timestamp) #:nodoc:
          delayed_job = Delayed::Job.enqueue(JobWrapper.new(job.serialize), queue: job.queue_name, run_at: Time.at(timestamp))
          job.provider_job_id = delayed_job.id
          delayed_job
        end
      end

      class JobWrapper #:nodoc:
        attr_accessor :job_data

        def initialize(job_data)
          @job_data = job_data
        end

        def perform
          Base.execute(job_data)
        end
      end
    end
  end
end

【讨论】:

【参考方案4】:

如果作业被取消,您可以模拟作业本身的取消,而不是将作业从队列中删除。

然后,当您来运行GenerateReportJob 时,您可以先检查是否取消了报告。如果有,那么您可以销毁取消记录并退出报告生成。如果没有取消,那么您可以照常进行。

【讨论】:

是的,我可以。但是...使用Delayed::Job 的重点是它是ActiveRecord::Base 的后代。它在桌子上有一个 id - 所以在我与活动作业集成之前创建另一个列/模型/对象/取消概念似乎很愚蠢,我只能获得 id。 无论如何,问这个问题的目的只是为了看看是否有人知道如何获取 ID。不过看这里:(github.com/rails/rails/blob/master/activejob/lib/active_job/…) 似乎表明该工作将以任何方式返回,而不是延迟工作。所以我会破解。 是的,抱歉,很感谢我回答的问题略有不同。 没问题!感谢您的及时回复!

以上是关于Rails 4.2 从活动作业中获取延迟的作业 ID的主要内容,如果未能解决你的问题,请参考以下文章

Rails 3 延迟作业 - 升级到 Rails 3 会中断延迟作业任务

检查延迟的作业是不是在rails中运行

如何在 Rails 中使用延迟作业取消预定作业?

Rails + 延迟作业:存储自定义作业类的正确位置在哪里?

如何在 Rails 开发中忽略延迟作业查询日志记录

Rails/Rspec:测试延迟作业邮件