Powered by AppSignal & Oban Pro

Supervisor - lifecycle

notebooks/supervisor_lifecycle.livemd

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)