Supervisor - lifecycle
Mix.install([{:kino, "~> 0.9.4"}])
Introduction
Practicing supervision, reading the book https://elixirpatterns.dev
Define modules
defmodule SimpleGenServer do
use GenServer
def start_link(name) do
GenServer.start_link(__MODULE__, name, name: name)
end
# We need to override the default child_spec because we want to
# add a GenServer to the same Supervisor. In such a case, IDs must
# be different.
def child_spec(init_arg) do
Supervisor.child_spec(
%{
id: {__MODULE__, init_arg},
start: {__MODULE__, :start_link, [init_arg]}
},
[]
)
end
@impl true
def init(name) do
# to ensure that our terminate/2 callback is invoked
Process.flag(:trap_exit, true)
IO.puts("Starting GenServer: #{inspect(name)}")
{:ok, name}
end
@impl true
def terminate(_reason, name) do
# The terminate/2 callback is invoked when a process is trapping exits,
# and its supervisor sends it an exit signal
IO.puts("Shutting down GenServer: #{inspect(name)}")
:ok
end
end
defmodule ParentSupervisor do
use Supervisor
def start_link(init_arg) do
Supervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
end
@impl true
def init(_init_arg) do
IO.puts("Starting Supervisor: #{inspect(__MODULE__)}")
children = [
{SimpleGenServer, :gen_server1},
{ChildSupervisor, []},
{SimpleGenServer, :gen_server4}
]
Supervisor.init(children, strategy: :one_for_one)
end
end
defmodule ChildSupervisor do
use Supervisor
def start_link(init_arg) do
Supervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
end
@impl true
def init(_init_arg) do
IO.puts("Starting Supervisor: #{inspect(__MODULE__)}")
children = [
{SimpleGenServer, :gen_server2},
{SimpleGenServer, :gen_server3}
]
Supervisor.init(children, strategy: :one_for_all)
end
end
Failure scenarios 1
ParentSupervisor.start_link([])
Supervisor.stop(ParentSupervisor)
Failure scenarios 2
ParentSupervisor.start_link([])
GenServer.stop(:gen_server2, :brutal_kill)
GenServer.stop(:gen_server1, :brutal_kill)
GenServer.stop(ChildSupervisor, :brutal_kill)