Długo działające opóźnione zadania pozostają zablokowane po ponownym uruchomieniu na Heroku
Po ponownym uruchomieniu Heroku worker (na polecenie lub w wyniku wdrożenia), Heroku wysyła SIGTERM
do procesu worker. W przypadku delayed_job
,SIGTERM
sygnał jest przechwytywany, a następnie worker przestaje wykonywać po zatrzymaniu bieżącego zadania (jeśli takie istnieje).
Jeśli robotnik zajmie dużo czasu, to Heroku wyśle SIGKILL
. W przypadku delayed_job
, pozostawia to zablokowane zadanie w bazie danych, które nie zostanie odebrane przez innego pracownika.
Chciałbym zapewnić, że zadania ostatecznie kończą się (chyba że jest błąd). Biorąc to pod uwagę, jaki jest najlepszy sposób, aby podejść do tego?
Widzę dwie opcje. Ale chciałbym dostać inny wkład:- zmodyfikuj
delayed_job
, aby przestać pracować nad bieżącym zadaniem (i zwolnić blokadę), gdy otrzymaSIGTERM
. - wymyśl (programowy) sposób wykrywania osieroconych zablokowanych zadań, a następnie odblokuj je.
6 answers
TLDR:
Umieść to na górze swojej metody pracy:
begin
term_now = false
old_term_handler = trap 'TERM' do
term_now = true
old_term_handler.call
end
I
Upewnij się, że jest wywoływany co najmniej raz na dziesięć sekund:
if term_now
puts 'told to terminate'
return true
end
I
Na koniec swojej metody, umieść to:
ensure
trap 'TERM', old_term_handler
end
Explanation:
Miałem ten sam problem i natknąłem się na Ten artykuł Heroku .
Praca zawierała zewnętrzną pętlę, więc podążałem za artykułem i dodałem trap('TERM')
i exit
. Jednak delayed_job
określa to jako failed with SystemExit
i oznacza, że zadanie nie powiodło się.
With the SIGTERM
now trapped by our trap
funkcja obsługi pracownika nie jest wywoływana, a zamiast tego natychmiast uruchamia ponownie zadanie, a następnie otrzymuje SIGKILL
kilka sekund później. Wracamy do punktu wyjścia.
Wypróbowałem kilka alternatyw dla exit
:
A
return true
oznacza zadanie jako pomyślne (i usuwa je z kolejki), ale cierpi na ten sam problem, jeśli jest inne zadanie czekam w kolejce.Wywołanie {[12] } zakończy zadanie i workera, ale nie pozwala robotnikowi usunąć zadania z kolejki, więc nadal masz problem z "osieroconymi zablokowanymi zadaniami".
-
Przed rozpoczęciem potencjalnie długiego zadania dodajemy nowy handler przerwań dla
'TERM'
wykonująctrap
(zgodnie z opisem w artykule Heroku) i używamy go do Ustawieniaterm_now = true
.Ale musimy również pobrać
old_term_handler
, który opóźniony kod pracownika pracy ustawiony (który jest zwracany przeztrap
) i pamiętaj, abycall
to. -
Nadal musimy zapewnić powrót kontroli do
Delayed:Job:Worker
z wystarczającym czasem na jej wyczyszczenie i wyłączenie, więc powinniśmy sprawdzaćterm_now
co najmniej (tuż poniżej) co dziesięć sekund ireturn
jeśli jest totrue
.Możesz albo
return true
alboreturn false
w zależności od tego, czy chcesz, aby praca była uznana za udaną, czy nie. Na koniec jest Ważne , aby pamiętać o usunięciu obsługi i zainstalowaniu z powrotem
Delayed:Job:Worker
Po zakończeniu. Jeśli tego nie zrobisz, zachowasz zwisające odniesienie do dodanego przez nas, co może skutkować wyciekiem pamięci, jeśli dodasz jeszcze jedną (na przykład, gdy pracownik ponownie rozpocznie tę pracę).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-03 16:57:07
Przerwij zadanie czysto na SIGTERM
Znacznie lepsze rozwiązanie jest teraz wbudowane w delayed_job. Użyj tego ustawienia, aby rzucić wyjątek dla sygnałów TERM, dodając go do inicjalizatora:
Delayed::Worker.raise_signal_exceptions = :term
Z tym ustawieniem, zadanie zostanie prawidłowo wyczyszczone i zakończone przed heroku wydaniem ostatecznego sygnału KILL przeznaczonego dla procesów niewspółpracujących:
Może być konieczne zgłaszanie WYJĄTKÓW na sygnałach SIGTERM, Delayed:: Worker.raise_signal_exceptions =: term spowoduje, że pracownik podnieść Sygnalexception powodując uruchomione zadanie do przerwania i być odblokowane, co sprawia, że zadanie dostępne dla innych pracowników. Domyślną wartością tej opcji jest false.
Możliwe wartości dla raise_signal_exceptions
to:
-
false
- żadne wyjątki nie zostaną podniesione (domyślnie) -
:term
- wywoła wyjątek tylko na sygnałach TERM, ale INT będzie czekał na zakończenie bieżącego zadania. -
true
- wywoła wyjątek w terminie i INT
Dostępny od wersji 3.0.5.
Zobacz: https://github.com/collectiveidea/delayed_job/commit/90579c3047099b6a58595d4025ab0f4b7f0aa67a
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-19 16:55:51
Do tego właśnie służy max_run_time
: po upłynięciu max_run_time
od czasu zablokowania zadania, inne procesy będą mogły uzyskać blokadę.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-26 01:17:38
Nowy na stronie, więc nie mogę skomentować Posta Dave ' a i muszę dodać nową odpowiedź.
Problem z podejściem Dave ' a polega na tym, że moje zadania są długie (minuty do 8 godzin) i wcale nie są powtarzalne. Nie mogę "zapewnić, aby dzwonić" co 10 sekund. Poza tym, próbowałem odpowiedzi Dave ' a, i zadanie jest zawsze usuwane z kolejki, niezależnie od tego, co zwracam-prawda czy fałsz. Nie wiem, jak utrzymać pracę w kolejce.
Zobacz to to żądanie pull . Myślę, że to może mi się udać. Prosimy o komentarz i wsparcie pull request.
Obecnie eksperymentuję z pułapką, a następnie ratuję sygnał wyjścia... Na razie bez powodzenia.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-30 00:09:17
Skończyło się na tym, że musiałem to zrobić w kilku miejscach, więc stworzyłem moduł, który trzymam w lib/, a następnie uruchamiam ExitOnTermSignal.wykonaj { long_running_task } z bloku wykonaj mojego opóźnionego zadania.
# Exits whatever is currently running when a SIGTERM is received. Needed since
# Delayed::Job traps TERM, so it does not clean up a job properly if the
# process receives a SIGTERM then SIGKILL, as happens on Heroku.
module ExitOnTermSignal
def self.execute(&block)
original_term_handler = Signal.trap 'TERM' do
original_term_handler.call
# Easiest way to kill job immediately and having DJ mark it as failed:
exit
end
begin
yield
ensure
Signal.trap 'TERM', original_term_handler
end
end
end
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-12 23:48:43
Używam maszyny stanowej do śledzenia postępu zadań i sprawiam, że proces jest idempotentny, dzięki czemu mogę wielokrotnie wywoływać perform na danym zadaniu/obiekcie i mieć pewność, że nie zastosuje on ponownie destrukcyjnej akcji. Następnie zaktualizuj zadanie rake / delayed_job, aby zwolnić termin logowania.
Po ponownym uruchomieniu proces będzie kontynuowany zgodnie z przeznaczeniem.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-04 08:35:03