-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Closed
Description
Environment
- Elixir & Erlang versions (elixir --version): Elixir 1.6.1 (compiled with OTP 20)
- Operating system: macOS 10.13.2
Current behavior
When using a DynamicSupervisor with :extra_arguments
, a crash in one child kills all other children and restarts the supervisor.
Here is a slightly shortened example iex session to reproduce the behaviour, the corresponding code will be given below:
$ iex -S mix
iex(1)> Application.ensure_all_started(:sasl)
iex(2)> Demo.Supervisor.start_child(:foo, :bar, :baz)
iex(3)> Demo.Supervisor.start_child(:foo, :bar, :baz)
iex(4)> {:ok, pid} = Demo.Supervisor.start_child(:foo, :bar, :baz)
iex(5)> Supervisor.which_children(Demo.Application.Supervisor)
[{Demo.Supervisor, #PID<0.116.0>, :supervisor, [Demo.Supervisor]}]
iex(6)> Supervisor.which_children(Demo.Supervisor)
[
{:undefined, #PID<0.128.0>, :worker, [Demo.Worker]},
{:undefined, #PID<0.130.0>, :worker, [Demo.Worker]},
{:undefined, #PID<0.132.0>, :worker, [Demo.Worker]}
]
iex(7)> Process.exit(pid, :kill)
=SUPERVISOR REPORT==== 19-Feb-2018::20:06:49 ===
Supervisor: {local,'Elixir.Demo.Application.Supervisor'}
Context: child_terminated
Reason: shutdown
Offender: [{pid,<0.116.0>},
{id,'Elixir.Demo.Supervisor'},
{mfargs,
{'Elixir.Demo.Supervisor',start_link,[implicit_arg]}},
{restart_type,permanent},
{shutdown,infinity},
{child_type,supervisor}]
(...)
iex(8)> Supervisor.which_children(Demo.Application.Supervisor)
[{Demo.Supervisor, #PID<0.137.0>, :supervisor, [Demo.Supervisor]}]
iex(9)> Supervisor.which_children(Demo.Supervisor)
[]
iex(10)>
As you can see, killing one child crashes the Dynamic Supervisor and with it all of its children.
Expected behavior
The Supervisor should restart the child without crashing.
Example Code
The example code can also be found here as a ready-to-run example project. I tried to stick as close very closely to the example from the DynamicSupervisor documentation.
defmodule Demo.Application do
use Application
def start(_type, _args) do
children = [{Demo.Supervisor, :implicit_arg}]
opts = [strategy: :one_for_one, name: __MODULE__.Supervisor]
Supervisor.start_link(children, opts)
end
end
defmodule Demo.Supervisor do
use DynamicSupervisor
def start_link(arg) do
DynamicSupervisor.start_link(__MODULE__, arg, name: __MODULE__)
end
def start_child(foo, bar, baz) do
# This will start child by calling Worker.start_link(implicit_arg, foo, bar, baz)
spec = Supervisor.Spec.worker(Demo.Worker, [foo, bar, baz])
DynamicSupervisor.start_child(__MODULE__, spec)
end
def init(implicit_arg) do
DynamicSupervisor.init(
strategy: :one_for_one,
extra_arguments: [implicit_arg]
)
end
end
defmodule Demo.Worker do
use GenServer
require Logger
def start_link(:implicit_arg, :foo, :bar, :baz) do
GenServer.start_link(__MODULE__, [:implicit_arg, :foo, :bar, :baz])
end
def init(:implicit_arg, :foo, :bar, :baz) do
{:ok, []}
end
def handle_call(request, from, state) do
Logger.debug(fn -> "#{request} from #{from}" end)
{:reply, :ok, state}
end
end
Metadata
Metadata
Assignees
Labels
No labels