5

我使用 Rails 4.2 和delayed_job 4.0.6 作为我的ActiveJob 后端。

我有一份工作,我只想在队列中允许一次。有问题的作业需要一分钟多的时间才能运行。它由模型上的回调排队。回调将比作业完成的频率高得多。该作业无需在未来多次排队。

这是我想要完成的一些伪代码。

# app/jobs/refresh_account_cache_job.rb
class RefreshAccountCacheJob < ActiveJob::Base
  def before_enqueue
    skip if job_already_enqueued
  end

  def perform
    Account.write_to_cache
  end

  def job_already_enqueued
    # ?
  end
end

如果作业的实例在再次调用时正在运行,则它仍应排队以备将来使用。我正在寻找一种方法让作业排队等待未来最多运行 1 次。

我认为答案必须特定于delayed_job,但如果它可以推广到ActiveJob,那就更好了。

4

2 回答 2

2

这可能不是完全合适的,但它应该让你指向正确的方向:

def self.up
  create_table :delayed_jobs, :force => true do |table|
  table.integer  :priority, :default => 0, :null => false
  table.integer  :attempts, :default => 0, :null => false
  table.text     :handler,                 :null => false
  table.text     :last_error
  table.datetime :run_at
  table.datetime :locked_at
  table.datetime :failed_at
  table.string   :locked_by
  table.string   :queue
  table.timestamps
end

因此,您可以向该表添加一个状态列,然后运行这样的查询来获取作业并在执行其他任何操作之前检查其状态。

Delayed::Job.where(queue: '<YOUR QUEUE>').where(id: params[:id]).status

你会问,你会如何设置状态?好吧,在延迟的工作中使用成功钩子。它看起来有点像这样:

def success(job)
  update_status('success')
end

private

def update_status(status)
  job = Job.find job_id
  job.status = status
  job.save!
end

希望这可以帮助!

于 2015-03-03T21:45:22.790 回答
1

我将发布我最终所做的事情作为答案,以获得有关它的反馈。这只是我正在测试的一个可能的解决方案。

在作业中,我正在检查 Delayed::Job 列表以查看当前处理程序是否存在。如果是我跳过这份工作。

# queue_job.rb
class EnqueueJob < ActiveJob::Base
  queue_as :default

  def already_enqueued?
    Delayed::Job.all.any? do |job|
      job.handler.include?("EnqueueJobHandler")
    end
  end

  def perform
    unless already_enqueued?
      # do stuff
    end
  end
end

到目前为止,它可以防止工作超出队列。缺点是我不知道我是否将缓存保持为我想要的最新状态。

于 2015-03-05T20:01:09.857 回答