I have Elixir Task, which takes some time (10 seconds). When the application is upgrading, this task is killed by Task.Supervisor
despite on shutdown: 30000
:
=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}]
I don't know how to gracefully stop the task (wait until task is complete) on application upgrade. Here is the code describing my 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
Is there any thoughts or assumptions how to wait until MyApp.TaskRunner.perform
finishes and then allow to stop the task?
It doesn't matter for me how to process tasks: using native Elixir's Task
or via own some TaskProcessor module.
Task.Supervisor.async
links tasks to caller, which might be a problem. However, I tried several times different cases with async
and start_link
and each time got the same result. My latest test was:
children = [
supervisor(Task.Supervisor, [[name: :tasks_sup, shutdown: 30000]]),
worker(MyApp.Worker, [state], restart: :permanent)
]
supervise(children, strategy: :one_for_one)
and
Task.Supervisor.start_child(:tasks_sup, MyApp.TaskRunner, :perform, [data])
The worker was killed after about 2-3 secs.
The links may be the ones bringing your tasks down. Because the worker calls Task.Supervisor.async
, it will link the task to your worker. The worker has a timeout of 5000 miliseconds, so it will be shutdown before the supervisor, killing the tasks. You can confirm this by setting the earlier reports.
Btw, you should only call async
if you are calling await
later by the same process and it doesn't seem to be the case here. You should likely call Task.Supervisor.start_child
instead (so the tasks are not linked to the caller).
Another possible reason your functions are getting killed is that the VM only keeps the latest two code module versions. If you upgrade twice in a short-interval, the old versions will be purged and their running processes killed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With