Rails: Cancelling a scheduled job in Sidekiq
Asked Answered
G

3

8

So I have a Sidekiq worker in my model which looks like this:

class Perk < ActiveRecord::Base

include Sidekiq::Worker
include Sidekiq::Status::Worker

after_save :update_release_time

def update_release_time
  if self.release_time_changed?
    #if scheduled job already exists then cancel and reschedule
    #  Sidekiq::Status.cancel scheduled_job_id
    #  scheduled_job_id = NotifierWorker.perform_at(time.seconds.from_now, .....)
    #elsif scheduled job doesn't exist, then schedule for the first time
    #  scheduled_job_id = NotifierWorker.perform_at(time.seconds.from_now, .....)
    #end
  end
end
end

So basically, my code checks if the release time has changed. If it has, then it has to cancel the previously scheduled job and schedule it at a new time. How do I achieve this i.e what would go in place of my pseudo-code? How do I check if scheduled_job_id exists and then fetch its id?

Generalization answered 12/6, 2014 at 9:27 Comment(0)
S
24

The API documentation has an overview of what you can do but you really need to dive into the source to discover all the capabilities.

You can do this but it won't be efficient. It's a linear scan for find a scheduled job by JID.

require 'sidekiq/api'
Sidekiq::ScheduledSet.new.find_job(jid).try(:delete)

Alternatively your job can look to see if it's still relevant when it runs.

Suggest answered 12/6, 2014 at 16:10 Comment(2)
There is a typo: ScehduledSet -> ScheduledSetMuricate
This is the way to do it. Just have it check to see if it is still relevant when it runs.Goss
D
1

The pseudocode that you wrote should work, though I'd remove the if/else block. Sidekiq::Status.cancel will simply return false if the item was not found. So the following pseudocode should be fine:

1) Cancel scheduled_job_id if scheduled_job_id.present?

2) Run NotifierWorker.perform_at ... - this is done regardless of whether you cancel or not anyway.

However, I will note, as @mike-perham stated, it will be slow (a linear search). As such, when I implemented Sidekiq::Status.cancel I added an optional second parameter for the timestamp. If you pass a timestamp then Redis will find scheduled tasks matching that time using binary search, so it only has to search linearly among the items scheduled at the exact same time.

As such, when cancelling you should run:

Sidekiq::Status.cancel(self.scheduled_job_id, self.release_time_was)

Drice answered 24/7, 2014 at 15:7 Comment(0)
M
1

Use a UUID saved in a DB or cache to make sure that you still need to run a job.

class SmartWorker
  include Sidekiq::Worker
  sidekiq_options :queue => :low,
                  :retry => false,
                  :backtrace => false

  def self.schedule id
    uuid = SecureRandom.uuid
    Redis.new.set("#{id}-key", uuid)
    SmartWorker.perform_in(1.minute, id, uuid)
  end

  def perform(id, uuid, force=false)
    return unless uuid == Redis.new.get("#{id}-key")
    if force || CronLockService.lock("lock", 5000)
      begin
        Model.find(id).relation.find_each{|it|
          Service.do_it(it)
        }
      ensure
        CronLockService.expire("lock")
      end
    end
  end
end

So if this happened, it would only run once

SmartWorker.schedule 1    
SmartWorker.schedule 1
Monroemonroy answered 27/4, 2017 at 21:57 Comment(3)
In your case, you could pass in the timestamp when the job is scheduled, and then check the db to make sure the timestamp is the same before running the job.Monroemonroy
Be careful, passing timestamp as arguments to workers ; I had to Time#round them before comparing. The Sidekiq doc says : >The arguments you pass to perform_async must be composed of simple JSON datatypes: string, integer, float, boolean, null, array and hash. [...more explantions shorten for the comment...]. Don't pass symbols, named parameters or complex Ruby objects (like Date or Time!) as those will not survive the dump/load round trip correctly.Mariammarian
I guess when I was thinking of timestamps I was thinking of ms since epoch.Monroemonroy

© 2022 - 2024 — McMap. All rights reserved.