Elixir Supervisor: Rest for One
Rest for one is a supervisor strategy that restarts a failed child and every child started after it. Children started before it keep running untouched.
Three children start in list order
The children list names a, b, and c in that order. 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: :rest_for_one)
IO.inspect(GenServer.call(:a, :get))
IO.inspect(GenServer.call(:b, :get))
IO.inspect(GenServer.call(:c, :get))
Supervisor.stop(sup)
A middle failure restarts the rest
All three children are raised by one. Then b fails, so b and c restart while a keeps 11.
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: :rest_for_one)
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 first failure restarts everything after it
All three children are raised by one. Then a fails, so a, b, and c all start 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: :rest_for_one)
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)
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: :rest_for_one)
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)
Key points
- Order: children start in the order they appear in the children list.
- Rest for one: the rule that restarts a failed child plus all children started after it.
- Earlier children: children started before the failed child are not restarted.
- Later children: children started after the failed child are restarted even though they did not fail.
- Restarted state: every restarted child starts again from its first value.
In this example, a rest for one supervisor starts a, b, and c, and all three are raised by one. Child c is last, so when c fails, only c restarts. After the failure, a still answers 11, b still answers 21, and c answers 30 again. The supervisor is stopped at the end.
Note
rest for one fits children that depend on the ones started before them.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
A rest for one supervisor runs children a, b, and c in that order. Child b fails. Which children restart?