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

37

我正在安排一个任务,在大约10分钟后运行。如何正确取消这个特定的任务,而不使用任何脏字段在模型中等等。是否有调用来删除特定任务或与特定模型、实例相关的任务?

我要安排一个任务,在10分钟后运行。如果想要取消这个任务,没有必要在模型中添加额外的脏字段等。是否有调用可以删除特定任务或与特定模型、实例相关的任务呢?请保留原文中的html标签。
4个回答

54

免责声明:我不是延迟任务(delayed_job)的专家...

"是否有任何调用可以删除特定的工作,或与特定模型、实例等相关的工作?"

Delayed::Job只是一个ActiveRecord对象,因此您可以查找和销毁其中的任何记录。根据您的使用情况,这可能以不同的方式处理。如果有人要手动销毁它们,则可以通过Web应用程序中的管理员界面处理。

# list all jobs
Delayed::Job.all
# find a job by id
job = Delayed::Job.find(params[:id])
# delete it
job.delete

如果您需要按“作业类型”删除作业的某些外部处理任务,则可以循环遍历每个作业并在匹配您的作业时将其删除;请尝试在脚本/控制台中执行此操作。

class MyJob < Struct.new(:some_value);
    def perform
        # ...
    end
end

my_job = MyJob.new('xyz')
job = Delayed::Job.enqueue(my_job, 0, 1.hour.from_now)
job.name
# => "MyJob"
job.handler
# => "--- !ruby/struct:MyJob \nsome_value: xyz\n"

所以,如果您想删除所有类型为MyJob的作业

Delayed::Job.all.each do |job|
    if job.name == "MyJob" then
        job.delete
    end
end

这可能对你的情况有所帮助,但也可能没有。在许多情况下,您可能只想删除一个MyJob,但仅在:some_value属性为'abc'而不是'xyz'时才这样做。在这种情况下,您可能需要在MyJob对象上实现“display_name”。如果存在,job.name将使用此内容。

class MyJob < Struct.new(:user_id);
    def perform
        # ...
    end

    def display_name
        return "MyJob-User-#{user_id}"
    end 
end

# store reference to a User
my_job = MyJob.new(User.first.id) # users.id is 1
job = Delayed::Job.enqueue(my_job, 0, 1.hour.from_now)
job.name
# => "MyJob-User-1"
job.handler
# => "--- !ruby/struct:MyJob \nuser_id: 1\n"

这样你就可以更有选择性地删除记录了?

希望这为你提供了足够的信息来处理它的可能方法?


11
建议使用“销毁”而不是“删除”。 - Brandon Bloom
1
为了循环遍历所有延迟的作业,您需要执行 Delayed::Job.all.each do |job|。(需要 .each)。 - Vox
据我所知,这不会停止/取消已经在执行的作业,对吗? - Pere Joan Martorell
@PereJoanMartorell - 您是正确的,这不会取消已经运行的作业,我知道的唯一方法是停止DJ进程,然后删除作业记录。 - house9

7

delayed_job 3引入了一个queue属性。这可以被劫持以安排可取消的工作。

class MyJob < Struct.new(:user_id)
  def self.queue_name
    "something-unique"
  end

  def perform
    # ...
  end
end

#scheduler
my_job = MyJob.new(User.first.id)
#'cancel' pending jobs first
Delayed::Job.where(queue: my_job.class.queue_name).destroy_all
#queue it up
Delayed::Job.enqueue(my_job,
  queue: my_job.class.queue_name,
  run_at: 1.hour.from_now
)

我真的不建议以这种方式处理事情。queue旨在使应用程序具有可扩展性。您已经使用自定义结构体完成了操作,只需选择一个不同的属性名称,就像house9所做的那样。 - Dex

0
您还可以考虑使用延迟作业的payload_object方法,如果您只是想通过传递的参数查找工作。
Delayed::Job.all.each do |job|
  job.destroy if job_corresponds_to_target?(job, target)
end

def job_corresponds_to_target?(job, target)
  job.payload_object.args.first == target.id
end

这个简单的例子没有充分利用返回的payload_object:

=> #<Delayed::PerformableMethod:0x0056551eae3660 @object=ReminderMailJob, @method_name=:perform_later, @args=[3]> 


0

我认为循环遍历所有排队的作业序列化字段(:handler)可能会变得昂贵,特别是当队列很大时(例如当Rails事件存储库重放您订阅以安排作业的事件时)。

因此,对我来说似乎有效的解决方案是避免手术,看起来像这样:

# some_specific_event_handler.rb or policy 

record_uuid = SomeModel.find(event.data[:id]).uuid
queue_name = "#{record_uuid}_update_notification"
Delayed::Job.where(queue: queue_name).destroy_all
UpdateNotificationJob.set(
  wait: 30.minutes,
  queue: queue_name,
).perform_later(record_uuid)

Delayed::Job 是一个 Delayed::Backend::ActiveRecord

:queue 字段只是一个字符串。 我认为它的值并不影响工作何时以及如何执行,除非您的代码对其进行了某些操作。

所以我将我的应用逻辑钩入到 :queue 字段值中,并且它适用于我的情况,其中,根据要求:

  • 如果发出新事件,则安排一项工作
  • 如果已经为此事件安排了任何相同类别的工作,则取消这些工作。

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