Rails 4.2中如何从active job获取delayed job id

14

有什么办法可以从ActiveJob排队中获取Delayed::Job的ID吗?当我排队一个任务时,我会得到一个ActiveJob::Base实例和一个@job_id,但这个作业ID似乎是内部使用的。到目前为止,我最好的猜测就是遍历最近创建的作业:

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

但那看起来很不专业。有些惊讶 ActiveJob 没有从 Delayed::Job 返回 ID,尤其是当作业被排队时明确返回该ID。

== 编辑

看起来我不是唯一一个遇到这个问题的人 (https://github.com/rails/rails/issues/18821)


您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Shadwell
2
这是为了在报告进入队列之前能够取消它们的能力。 - kddeisz
4个回答

12

如果以后有人发现这个问题: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
@Alexander 通常这会放在初始化器中。类似于 config/initializers/delayed_job.rb - kddeisz
在该补丁发布之前,我遇到了这个问题,并制作了一个宝石来通过ActiveJob查询作业状态:https://github.com/cdale77/active_job_status - Brad Johnson
1
此外,您可能需要将attr_accessor:provider_job_id添加到ApplicationJob(如果您尚未这样做),或者添加到每个作业类中。 将@kddeisz的代码添加到初始化程序中非常有效。 - Brendon Muir
1
如果对任何人来说还不够清楚,在Rails 5中,您只需说@job.provider_job_id。 - Greg Blass

4

受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) 最后,我们可以对 delayed_job 进行任何需要的操作,例如删除它:

delayed_job.delete

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

@kddeisz:我只测试了Beguene的答案(因为它更接近Rails 5代码,而且你的答案对我来说太聪明了,难以理解;但看起来很棒!)。在Beguene的答案中,只缺少了一个微小的成分:attr_accessor :provider_job_id语句。更好的选择是向Beguene的答案添加注释,但由于我是stackoverflow的新手,并且没有声望,我无权添加评论。因此,我想,回答这个问题并提供所有我需要使其工作的成分是有意义的。 - Olli
这很好,如果你想在“enqueue”回调中获取提供者ID(延迟作业ID),但是它不会在执行时(在“perform”回调中)给你ID。 - phil

2
我使用 Rails 4.2 成功地使用了来自 Rails 5 的新补丁,方法如下:
创建文件 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

0

如果取消工作,您可以将其建模为工作本身的取消,而不是从队列中删除该工作。

然后,在运行GenerateReportJob之前,您可以首先检查报告是否已取消。如果有一个取消记录,则可以销毁该取消记录并退出报告生成。如果没有取消,则可以像往常一样继续进行。


可以,但是使用Delayed::Job的重点在于它是ActiveRecord::Base的后代。它在表上有一个id - 因此,在我与active job集成之前,只需获取id就能取消任务,创建另一个列/模型/对象/概念似乎有些愚蠢。 - kddeisz
无论如何,提出这个问题的目的只是想看看是否有人知道如何获取ID。不过在这里看到:(https://github.com/rails/rails/blob/master/activejob/lib/active_job/enqueuing.rb#L74)似乎表明该作业将以任何方式返回,而不是延迟作业。所以我会进行一些修改。 - kddeisz
是的,抱歉,我明白我回答了一个稍微不同的问题。 - Shadwell
没问题!我很感激您的及时回复! - kddeisz

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