How to cancel scheduled job with delayed_job in Rails?

后端 未结 4 1064
予麋鹿
予麋鹿 2020-12-07 17:49

I am scheduling a job to run in say, 10 minutes. How to properly cancel this particular job without using any kind of dirty extra fields in model and so on. Is there any cal

相关标签:
4条回答
  • 2020-12-07 18:12

    You may also consider using delayed job's payload_object method, if you're looking for a job through its passed parameter only.

    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
    

    This simplist example do not use fully the payload_object returned:

    => #<Delayed::PerformableMethod:0x0056551eae3660 @object=ReminderMailJob, @method_name=:perform_later, @args=[3]> 
    
    
    0 讨论(0)
  • 2020-12-07 18:16

    disclaimer: I am not an expert user of delayed_job...

    "Is there any call to remove particular job, or jobs related to specific model, instance, etc?"

    Delayed::Job is just an ActiveRecord object so you can find and destroy any of those records. Depending on your use case this could be handled different ways. If someone is going to manually destroy them this could be handled through an admin interface in your web app.

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

    if you need some out of process task deleting jobs by 'job type' you could loop through each one and delete it if it matches your job; try this in script/console

    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"
    

    so given the above if you wanted to delete all jobs of type MyJob

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

    this may or may not help for your situation? in many cases you might want to delete a MyJob but only where the :some_value attribute was 'abc' and not 'xyz'. In this case you might need to implement a 'display_name' on your MyJob object. job.name will use this if it exists

    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"
    

    this way you could be more selective about which records to delete?

    hopefully this gives you enough information on possible ways to handle it?

    0 讨论(0)
  • 2020-12-07 18:17

    I think it may get pricey to loop through all queued jobs serialized field (:handler), especially when queue is large (for example when rails eventstore replays events that you've subscribed to to schedule a job).

    So the solution that seems to work for me, avoiding surgeries, looks like this:

    # 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 is a Delayed::Backend::ActiveRecord

    The :queue field is just any string. I don't think its value plays any role in when and how job will be executed unless your code does something with it.

    So I hooked my app logic to that :queue field value and it worked for my case, where, by requirements:

    • if new event was emitted then schedule a job
    • if there had been any same class jobs scheduled for this event, then dump those.
    0 讨论(0)
  • 2020-12-07 18:30

    delayed_job 3 introduced a queue attribute. This can be hijacked to schedule a cancelable job.

    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
    )
    
    0 讨论(0)
提交回复
热议问题