🎓 A1Learner
🔍
0 of 46 lessons completed

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?