Ho iniziato a lavorare con Sidekiq di recente e ho notato che ha una caratteristica impressionante che ho cercato per molto tempo:Ruby on Rails + Sidekiq: Come cambio un orario di inizio del lavoro pianificato?
UserMailer.delay_until(5.days.from_now).find_more_friends_email
Fondamentalmente posso pianificare un lavoro in futuro, quindi non ho bisogno del mio app per sondare continuamente nuovi eventi con un'ora di inizio.
Ora funziona come un incantesimo, ma come posso cambiare l'ora di inizio di un lavoro? Spesso alcuni eventi programmati avranno il loro orario di inizio cambiato. Come posso replicare questo in sidekiq?
So che posso eliminare il lavoro e crearne uno nuovo, ma è possibile modificare solo l'ora di inizio?
EDIT:
ho costruito su un'idea di Oto Brglez ed ecco il codice documentato:
module TaskStuff
class TaskSetter
include Sidekiq::Worker
sidekiq_options retry: false
def perform(task_id)
task = Task.find(task_id)
# Get the worker that's performing this job
if worker = AppHelpers.find_worker(jid)
# If the worker matches the "at" timestamp then this is the right one and we should execute it
if worker.last["payload"]["at"].to_s.match(/(\d*\.\d{0,3})\d*/)[1] == task.start.to_f.to_s.match(/(\d*\.\d{0,3})\d*/)[1]
task.execute
else
custom_logger.debug("This worker is the wrong one. Skipping...")
end
else
custom_logger.error("We couldn't find the worker")
end
end
end
end
module AppHelpers
[...]
def self.find_worker(jid)
Sidekiq::Workers.new.select {|e| e.last["payload"]["jid"] == jid}.first
end
[...]
end
> task = Task.create(start: 5.hours.from_now)
> TaskStuff::TastSetter.perform_at(task.start, task.id)
Ora, se faccio questo
> task.update_attributes(start: 4.hours.from_now)
> TaskStuff::TastSetter.perform_at(task.start, task.id)
il compito sarà ottenere eseguito in 4 ore e l'altro lavoro (che verrà eseguito in 5 ore) verrà ignorato e rimosso quando raggiunge il suo tempo.
Inizialmente, ho provato a utilizzare Time.now
anziché worker.last["payload"]["at"]
ma ciò avrebbe potuto essere piuttosto impreciso perché un processo pianificato non verrà sempre eseguito in tempo. Esiste un intervallo di controllo di 15 secondi e se tutti i lavoratori sono occupati altrove, il lavoro potrebbe essere ulteriormente ritardato.
Ho dovuto usare Regexp
per abbinare l'ora di inizio perché durante la lettura del task.start
avrei potuto ottenere un float con un diverso numero di decimali e la condizione se non passasse. In questo modo ottengo entrambi i valori su 3 decimali.
Ho scoperto che l'unico modo per ottenere l'attributo "at" del lavoro è farlo passare attraverso l'operatore. Se dovessi chiedere a Redis o usare Mike's Sidekiq::ScheduledSet.new
, non otterrei il lavoro corrente perché sarebbe già stato estratto da Redis.
EDIT 2:
Per chiunque sia interessato, sono andato con un approccio simile, ma diverso. Fondamentalmente invece di confrontare l'ora di inizio dello Task
, ho aggiunto un campo aggiuntivo al modello e alla chiamata Sidekiq, denominata start_token
. Se il lavoro sidekiq è stato chiamato con lo stesso token che l'oggetto ha allora è valido, altrimenti scartare e saltare il lavoro. Il token viene aggiornato ogni volta che il modello cambia start_time.
Sulla base della sua seconda modifica, e per coloro che questo può aiutare, come io sto usando un oggetto ActiveRecord dentro il mio lavoro, ho sto usando il campo updated_at. Lo sto passando al mio lavoro con il mio ID ActiveRecord e quando il mio lavoro inizia, controllo se il updated_at è sempre lo stesso – phyzalis