🎓 A1Learner
🔍
0 of 46 lessons completed

Elixir Supervisor: One for One

One for one is a supervisor strategy that restarts only the child that fails. The other children keep their state and keep running.

A supervisor starts both children

The children list names two workers. Both answer with 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}]}}
]
{:ok, sup} = Supervisor.start_link(children, strategy: :one_for_one)
IO.inspect(GenServer.call(:a, :get))
IO.inspect(GenServer.call(:b, :get))
Supervisor.stop(sup)

Only the failed child restarts

Both children are raised by one. Then child a fails, and only a starts again from 10.

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}]}}
]
{:ok, sup} = Supervisor.start_link(children, strategy: :one_for_one)
GenServer.cast(:a, :inc)
GenServer.cast(:b, :inc)
IO.inspect(GenServer.call(:a, :get))
IO.inspect(GenServer.call(:b, :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))
Supervisor.stop(sup)

Stopping the supervisor stops its children

The supervisor is stopped at the end of this block. Both children are gone with it.

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}]}}
]
{:ok, sup} = Supervisor.start_link(children, strategy: :one_for_one)
Supervisor.stop(sup)
IO.inspect(GenServer.whereis(:a) == nil)
IO.inspect(GenServer.whereis(:b) == nil)

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_one)
GenServer.cast(:a, :inc)
IO.inspect(GenServer.call(:a, :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

  • Supervisor: a process that starts children and restarts them when they fail.
  • Child: a worker process started from a child specification.
  • Child specification: a small map naming a child and how to start it.
  • Strategy: the rule a supervisor follows when a child fails.
  • One for one: the rule that restarts only the failed child.

In this example, a one for one supervisor starts three workers. Child a is raised to 11 and then fails. After the failure, a answers 10 again, while b and c still answer their untouched values 20 and 30. The supervisor is stopped at the end.

Note

a restarted child starts from its first value again, not from the value it had when it failed.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

A one for one supervisor runs children a and b. Child a fails. What happens?