Elixir Supervisor: One for All
One for all is a supervisor strategy that restarts every child when any one child fails. No child keeps its state after a failure, even the healthy ones.
Three children start together
The children list names a, b, and c. All three answer their starting values.
defmodule Worker do
use GenServer
def start_link({name, initial}), do: GenServer.start_link(__MODULE__, initial, name: name)
def init(initial), do: {:ok, initial}
def handle_call(:get, _from, state), do: {:reply, state, state}
def handle_cast(:inc, state), do: {:noreply, state + 1}
end
children = [
%{id: :a, start: {Worker, :start_link, [{:a, 10}]}},
%{id: :b, start: {Worker, :start_link, [{:b, 20}]}},
%{id: :c, start: {Worker, :start_link, [{:c, 30}]}}
]
{:ok, sup} = Supervisor.start_link(children, strategy: :one_for_all)
IO.inspect(GenServer.call(:a, :get))
IO.inspect(GenServer.call(:b, :get))
IO.inspect(GenServer.call(:c, :get))
Supervisor.stop(sup)
One middle failure restarts all
All three children are raised by one. Then b fails, and every child starts again.
defmodule Worker do
use GenServer
def start_link({name, initial}), do: GenServer.start_link(__MODULE__, initial, name: name)
def init(initial), do: {:ok, initial}
def handle_call(:get, _from, state), do: {:reply, state, state}
def handle_cast(:inc, state), do: {:noreply, state + 1}
end
children = [
%{id: :a, start: {Worker, :start_link, [{:a, 10}]}},
%{id: :b, start: {Worker, :start_link, [{:b, 20}]}},
%{id: :c, start: {Worker, :start_link, [{:c, 30}]}}
]
{:ok, sup} = Supervisor.start_link(children, strategy: :one_for_all)
GenServer.cast(:a, :inc)
GenServer.cast(:b, :inc)
GenServer.cast(:c, :inc)
IO.inspect(GenServer.call(:a, :get))
IO.inspect(GenServer.call(:b, :get))
IO.inspect(GenServer.call(:c, :get))
Process.exit(GenServer.whereis(:b), :kill)
Process.sleep(100)
Supervisor.which_children(sup)
IO.inspect(GenServer.call(:a, :get))
IO.inspect(GenServer.call(:b, :get))
IO.inspect(GenServer.call(:c, :get))
Supervisor.stop(sup)
A last failure restarts all too
All three children are raised by one again. Then c fails, and still every child starts again.
defmodule Worker do
use GenServer
def start_link({name, initial}), do: GenServer.start_link(__MODULE__, initial, name: name)
def init(initial), do: {:ok, initial}
def handle_call(:get, _from, state), do: {:reply, state, state}
def handle_cast(:inc, state), do: {:noreply, state + 1}
end
children = [
%{id: :a, start: {Worker, :start_link, [{:a, 10}]}},
%{id: :b, start: {Worker, :start_link, [{:b, 20}]}},
%{id: :c, start: {Worker, :start_link, [{:c, 30}]}}
]
{:ok, sup} = Supervisor.start_link(children, strategy: :one_for_all)
GenServer.cast(:a, :inc)
GenServer.cast(:b, :inc)
GenServer.cast(:c, :inc)
IO.inspect(GenServer.call(:a, :get))
IO.inspect(GenServer.call(:b, :get))
IO.inspect(GenServer.call(:c, :get))
Process.exit(GenServer.whereis(:c), :kill)
Process.sleep(100)
Supervisor.which_children(sup)
IO.inspect(GenServer.call(:a, :get))
IO.inspect(GenServer.call(:b, :get))
IO.inspect(GenServer.call(:c, :get))
Supervisor.stop(sup)
Example
defmodule Worker do
use GenServer
def start_link({name, initial}), do: GenServer.start_link(__MODULE__, initial, name: name)
def init(initial), do: {:ok, initial}
def handle_call(:get, _from, state), do: {:reply, state, state}
def handle_cast(:inc, state), do: {:noreply, state + 1}
end
children = [
%{id: :a, start: {Worker, :start_link, [{:a, 10}]}},
%{id: :b, start: {Worker, :start_link, [{:b, 20}]}},
%{id: :c, start: {Worker, :start_link, [{:c, 30}]}}
]
{:ok, sup} = Supervisor.start_link(children, strategy: :one_for_all)
GenServer.cast(:a, :inc)
GenServer.cast(:b, :inc)
GenServer.cast(:c, :inc)
IO.inspect(GenServer.call(:a, :get))
IO.inspect(GenServer.call(:b, :get))
IO.inspect(GenServer.call(:c, :get))
Process.exit(GenServer.whereis(:a), :kill)
Process.sleep(100)
Supervisor.which_children(sup)
IO.inspect(GenServer.call(:a, :get))
IO.inspect(GenServer.call(:b, :get))
IO.inspect(GenServer.call(:c, :get))
Supervisor.stop(sup)
Key points
- One for all: the rule that restarts all children when one child fails.
- Healthy children: children that did not fail are still restarted under this rule.
- Shared state: this rule fits children whose states belong together and must restart as a group.
- Restarted state: every child starts again from its first value.
- Failure trigger: a single failed child is enough to restart the whole group.
In this example, a one for all supervisor starts a, b, and c, and all three are raised by one. When a fails, the whole group restarts. After the failure, a, b, and c answer 10, 20, and 30 again. The supervisor is stopped at the end.
Note
one for all restarts more work than one for one, so it is used only when the children act as one unit.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
A one for all supervisor runs children a, b, and c. Child c fails. What happens?