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

Posted

技术标签:

【中文标题】如何在 Rails 中使用延迟作业取消预定作业?【英文标题】:How to cancel scheduled job with delayed_job in Rails? 【发布时间】:2011-04-07 23:38:03 【问题描述】:

我正在安排一个作业,例如 10 分钟后运行。如何在不使用模型中任何脏的额外字段的情况下正确取消此特定作业等。是否有任何要求删除特定工作或与特定模型、实例等相关的工作?

【问题讨论】:

【参考方案1】:

免责声明:我不是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"

这样您可以更有选择性地删除哪些记录?

希望这可以为您提供有关可能的处理方法的足够信息?

【讨论】:

你应该使用“destroy”而不是“delete”。 为了循环所有延迟的工作,你需要做Delayed::Job.all.each do |job|。 (需要 .each)。 据我所知,这不会停止/取消已经在执行的工作,对吧? @PereJoanMartorell - 你是对的,这不会取消已经运行的作业,我知道如何做到这一点的唯一方法是停止 DJ 进程,然后删除作业记录。【参考方案2】:

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 就可以了。【参考方案3】:

如果您仅通过其传递的参数查找作业,您也可以考虑使用延迟作业的 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]> 

【讨论】:

【参考方案4】:

我认为循环遍历所有排队的作业序列化字段 (:handler) 可能会很昂贵,尤其是当队列很大时(例如,当 rails eventstore 重播您已订阅以安排作业的事件时)。

所以似乎对我有用的解决方案,避免手术,看起来像这样:

# 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 字段值挂钩,它适用于我的情况,根据要求:

如果发出新事件,则安排作业 如果为此事件安排了任何相同的类作业,则转储这些作业。

【讨论】:

以上是关于如何在 Rails 中使用延迟作业取消预定作业?的主要内容,如果未能解决你的问题,请参考以下文章

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

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

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

如何在 Ruby on Rails 中完成延迟作业后执行 ajax 回调?

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

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