2015-02-17 26 views
5

Używam Rails 4.2 i delayed_job 4.0.6 jako mojego backendu ActiveJob.W Railsach 4.2 sprawdź, czy zadanie jest już zakolejkowane

Mam pracę, którą chciałbym jednorazowo zezwolić tylko w kolejce. Praca, o której mowa, trwa ponad minutę. Jest on składany przez wywołanie zwrotne na modelu. Oddzwanianie będzie uruchamiane znacznie częściej niż zadanie może zostać zakończone. Praca nie musi być kolejkowana więcej niż jeden raz w przyszłości.

Oto kilka pseudokod dla tego, co próbuję osiągnąć.

# app/jobs/refresh_account_cache_job.rb 
class RefreshAccountCacheJob < ActiveJob::Base 
    def before_enqueue 
    skip if job_already_enqueued 
    end 

    def perform 
    Account.write_to_cache 
    end 

    def job_already_enqueued 
    # ? 
    end 
end 

Jeśli instancja zadania jest uruchomiona, gdy jest wywoływana ponownie, powinna być nadal zakolowywana na przyszłość. Poszukuję sposobu, aby zlecić wykonanie kolejnej pracy dla przyszłego uruchomienia maksymalnie 1 raz.

Zakładam, że odpowiedź będzie musiała być specyficzna dla delayed_job, ale jeśli można ją uogólnić na ActiveJob, będzie jeszcze lepiej.

+0

Czy można użyć redis? Tak jak w przypadku, czy zainstalowałeś go na swoim stosie? –

+0

Muszę trzymać się Postgres w tym projekcie. –

+0

Nie prosząc o zastąpienie go, ale jeśli możesz używać redis wraz z Postgres, może być proste rozwiązanie. –

Odpowiedz

2

To może nie być dokładne dopasowanie, ale powinno Ci wskazał w dobrym kierunku:

def self.up 
    create_table :delayed_jobs, :force => true do |table| 
    table.integer :priority, :default => 0, :null => false 
    table.integer :attempts, :default => 0, :null => false 
    table.text  :handler,     :null => false 
    table.text  :last_error 
    table.datetime :run_at 
    table.datetime :locked_at 
    table.datetime :failed_at 
    table.string :locked_by 
    table.string :queue 
    table.timestamps 
end 

Więc można dodać kolumnę stanu do tej tabeli, a następnie uruchomić kwerendę takich jak to chwycić pracę i sprawdź jej stan, zanim zrobi cokolwiek innego.

Delayed::Job.where(queue: '<YOUR QUEUE>').where(id: params[:id]).status

Jak ustawić status, pytasz? Cóż, użyj haka sukcesów w opóźnionych zadaniach. Wyglądałoby to trochę tak:

def success(job) 
    update_status('success') 
end 

private 

def update_status(status) 
    job = Job.find job_id 
    job.status = status 
    job.save! 
end 

Mam nadzieję, że to pomoże!

+0

Próbuję. –

1

Publikuję, co zrobiłem, jako odpowiedź, aby uzyskać opinię na ten temat. To tylko możliwe rozwiązanie, które testuję.

W zadaniu sprawdzam listę Opóźnione :: Prace, aby sprawdzić, czy obecny program obsługi jest obecny. Jeśli to jest, pomijam pracę.

# queue_job.rb 
class EnqueueJob < ActiveJob::Base 
    queue_as :default 

    def already_enqueued? 
    Delayed::Job.all.any? do |job| 
     job.handler.include?("EnqueueJobHandler") 
    end 
    end 

    def perform 
    unless already_enqueued? 
     # do stuff 
    end 
    end 
end 

Do tej pory nie powoduje on przekroczenia kolejki. Minusem jest to, że nie wiem, że utrzymuję pamięć podręczną tak aktualną, jak chcę.

0

To może być zbyt uproszczone, ale dlaczego nie wystarczy dodać zmienną klasy, na przykład @@running, ustawić ją na true na before_enqueue i wyczyścić na końcu zadania?

Następnie można po prostu sprawdzić tę zmienną i pominąć kolejkę, jeśli jest ustawiona.