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?