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

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

【问题讨论】:

    标签: ruby-on-rails ruby ruby-on-rails-3 delayed-job


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

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

      【讨论】:

        猜你喜欢
        • 2013-11-26
        • 2020-04-05
        • 2012-04-20
        • 2014-10-04
        • 1970-01-01
        • 1970-01-01
        • 2023-04-03
        • 1970-01-01
        • 2013-05-01
        相关资源
        最近更新 更多