2015-08-12 22 views
6

Ho un compito di elisir, che richiede un po 'di tempo (10 secondi). Quando l'applicazione è l'aggiornamento, questo compito viene ucciso da Task.Supervisor nonostante il shutdown: 30000:Task Elixir - Arresto agevole

=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}] 

Non so come fermare con garbo il compito (attendere compito è completo) durante l'aggiornamento dell'applicazione. Qui è il codice che descrive il mio problema:

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 

C'è qualche pensiero o ipotesi come aspettare fino MyApp.TaskRunner.perform finiture e quindi consentire di fermare l'operazione?

Non ha importanza per me come elaborare le attività: utilizzando l'Elixir originale Task o tramite il proprio modulo TaskProcessor.

Task.Supervisor.async collega le attività al chiamante, che potrebbe essere un problema. Tuttavia, ho provato più volte casi diversi con async e start_link e ogni volta ho ottenuto lo stesso risultato. La mia ultima prova era:

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

supervise(children, strategy: :one_for_one) 

e

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

L'operaio è stato ucciso dopo circa 2-3 secondi.

risposta

1

I collegamenti possono essere quelli che portano giù i tuoi compiti. Poiché il lavoratore chiama lo Task.Supervisor.async, collegherà l'attività al proprio lavoratore. L'operatore ha un timeout di 5000 millisecondi, quindi verrà arrestato prima del supervisore, uccidendo le attività. Puoi confermare questo impostando i rapporti precedenti.

Btw, si dovrebbe chiamare solo async se si chiama await in seguito con lo stesso processo e non sembra essere il caso qui. Probabilmente dovresti chiamare lo Task.Supervisor.start_child (quindi le attività non sono collegate al chiamante).

Un altro possibile motivo per cui le funzioni vengono eliminate è che la VM mantiene solo le ultime due versioni del modulo di codice. Se esegui l'aggiornamento due volte in un intervallo breve, le versioni precedenti verranno eliminate e i processi in esecuzione verranno eliminati.