π° πππππ πππ π΄πππππππ πππ πΎπππππππππ ππππππ.
17 August 2025
A Supervisor is a process that supervises other processes (child processes). Supervisors are used to build a hierarchical process structure called a supervision tree.
In order to start a supervisor process, we need to create a child process that will be supervised. As an example, we will define a GenServer
that will store our application state as a hashmap. Other processes can store key/value pairs in this child process.
defmodule Hashmap do
use GenServer
def start_link(initial_state \\ %{}) do
GenServer.start_link(__MODULE__, initial_state, name: __MODULE__)
end
@impl true
def init(initial_state) do
{:ok, initial_state}
end
@impl true
def handle_call({:get, key}, _from, state) do
value = Map.get(state, key)
{:reply, value, state}
end
@impl true
def handle_call({:get, key, default}, _from, state) do
value = Map.get(state, key, default)
{:reply, value, state}
end
@impl true
def handle_call({:get_all}, _from, state) do
{:reply, state, state}
end
@impl true
def handle_call({:update, key, value}, _from, state) do
new_state = Map.put(state, key, value)
{:reply, :ok, new_state}
end
@impl true
def handle_call({:put, key, value}, _from, state) do
new_state = Map.put(state, key, value)
{:reply, :ok, new_state}
end
@impl true
def handle_cast({:delete, key}, state) do
new_state = Map.delete(state, key)
{:noreply, new_state}
end
end
We can now start a supervisor
that will start and supervise our hashmap
process. The first step is to define a list of child specifications that control how each child behaves. Each child specification is a map, as shown below:
children = [
# The Hashmap is a child started via Hashmap.start_link(%{app_name: "clivern"})
%{
id: Hashmap,
start: {Hashmap, :start_link, [%{app_name: "clivern"}]}
}
]
# Now we start the supervisor with the children and a strategy
{:ok, pid} = Supervisor.start_link(children, strategy: :one_for_one)
# After started, we can query the supervisor for information
Supervisor.count_children(pid)
Note that when starting the GenServer
, we are registering it with name Hashmap
via the name: __MODULE__
option. This allows us to call it directly.
# Get value of key equal to name
GenServer.call(Hashmap, {:get, :app_name})
# Update the name
GenServer.call(Hashmap, {:update, :app_name, "scuti"})
# Add the version
GenServer.call(Hashmap, {:put, :version, "v1.0.0"})
# Get all hashmap
GenServer.call(Hashmap, {:get_all})
# Delete the version
GenServer.cast(Hashmap, {:delete, :version})
However, if we send an invalid message, the hashmap server is going to crash:
GenServer.call(Hashmap, {:invalid_message})
Luckily, since the server is being supervised by a supervisor
, the supervisor
will automatically start a new one, reset back to its initial state. We can check the current state as follows:
GenServer.call(Hashmap, {:get_all})
# We can query the supervisor for information about current workers
Supervisor.count_children(pid)
For more info about the supervisor module, check the Elixir guide