2015-08-12 14 views
6

Mam zadanie Elixir, które zajmuje trochę czasu (10 sekund). Gdy aplikacja jest uaktualnieniem, zadanie to zostanie zabity przez Task.Supervisor mimo na shutdown: 30000:Zadanie z eliksirem - Zgrabne zamknięcie

=SUPERVISOR REPORT==== 13-Aug-2015::00:03:09 === 
Supervisor: {local,tasks_sup} 
Context: child_terminated 
Reason:  killed 
Offender: [{pid,<0.304.0>}, 
       {id,'Elixir.Task.Supervised'}, 
       {mfargs,{'Elixir.Task.Supervised',start_link,undefined}}, 
       {restart_type,temporary}, 
       {shutdown,30000}, 
       {child_type,worker}] 

Nie wiem, jak wdzięcznie zatrzymać zadanie (czekać aż zadanie jest zakończone) w sprawie uaktualnienia aplikacji. Oto kod opisujący mój problem:

defmodule MyApp do 
    use Application 

    def start(_, _) do 
    MyApp.Supervisor.start_link([]) 
    end 

end 

defmodule MyApp.Supervisor do 

    use Supervisor 

    def start_link(state) do 
    Supervisor.start_link(__MODULE__, state, name: __MODULE__) 
    end 

    def init(state) do 
    children = [ 
     supervisor(Task.Supervisor, [[name: :tasks_sup, shutdown: 30000]]), 
     worker(MyApp.Worker, [state], restart: :permanent) 
    ] 

    supervise(children, strategy: :one_for_one) 
    end 

end 

defmodule MyApp.Worker do 

    def start_link(state) do 
    GenServer.start_link(__MODULE__, state, [name: MyApp.Worker]) 
    end 

    def init(state) do 
    {:ok, state} 
    end 

    def handle_call(:which_children, _, state) do 
    children = [{Task.Supervisor, :tasks_sup, :supervisor, [Task.Supervisor]}] 
    {:reply, children, state} 
    end 

    def handle_info({:task, data}, state) do 
    Task.Supervisor.async(:tasks_sup, MyApp.TaskRunner, :perform, [data]) 
    end 

    def handle_info(_, state) do 
    {:noreply, state} 
    end 

end 

defmodule MyApp.TaskRunner do 

    def perform(data) do 
    # some 10 secs job 
    end 

end 

Czy istnieją jakieś myśli lub założenia jak czekać aż MyApp.TaskRunner.perform wykończeń, a następnie pozwolić, aby zatrzymać zadanie?

Nie ma dla mnie znaczenia, jak przetwarzać zadania: korzystanie z natywnego Elixir's Task lub przez własny moduł TaskProcessor.

Task.Supervisor.async łączy zadania z dzwoniącym, co może być problemem. Jednak próbowałem kilka razy różnych przypadków z async i start_link i za każdym razem otrzymałem taki sam wynik. Moja ostatnia próba była:

children = [ 
    supervisor(Task.Supervisor, [[name: :tasks_sup, shutdown: 30000]]), 
    worker(MyApp.Worker, [state], restart: :permanent) 
] 

supervise(children, strategy: :one_for_one) 

i

Task.Supervisor.start_child(:tasks_sup, MyApp.TaskRunner, :perform, [data]) 

Pracownik zginął po około 2-3 sekund.

Odpowiedz

1

Linki mogą być tymi, które obniżają Twoje zadania. Ponieważ pracownik wywołuje Task.Supervisor.async, połączy to zadanie z twoim pracownikiem. Pracownik ma limit czasu wynoszący 5000 milisekund, więc zostanie on zamknięty przed przełożonym, zabijając zadania. Możesz to potwierdzić, ustawiając wcześniejsze raporty.

Przy okazji powinieneś zadzwonić pod numer async, jeśli dzwonisz pod numer await w tym samym procesie i wydaje się, że nie ma to miejsca w tym przypadku. Zamiast tego prawdopodobnie powinieneś zadzwonić pod numer Task.Supervisor.start_child (aby zadania nie były połączone z osobą dzwoniącą).

Innym możliwym powodem, dla którego twoje funkcje są zabijane, jest to, że maszyna wirtualna zachowuje tylko najnowsze wersje dwóch modułów kodu. Jeśli dokonasz aktualizacji dwukrotnie w krótkim okresie, stare wersje zostaną wyczyszczone, a ich uruchomione procesy zabite.