Supervisors
“In a supervision hierarchy, keep important application state or functionality near the root while delegating risky operations towards the leaves.” - Error Kernel
If the child of a supervisor fails to restart max_restarts
times in max_seconds
, then the SUPERVISOR crashes. This means if a child of the application supervisor crashes, the ENTIRE APPLICATION will crash.
So yes fail if there are issues, but the known state should actually work.
Dynamic Supervisors
Works well with Registry to limit to one per or something
Init
defmodule ReactPhoenix.Dynamic.Supervisor do
use DynamicSupervisor
def init(:ok) do
DynamicSupervisor.init(strategy: :one_for_one)
end
#.....
end
:strategy
- Currently only:one_for_one
, where a child process terminates doesnt affect other children:max_restarts
- max restarts allowed in max_seconds. Default3
:max_seconds
- the time frame in which:max_restarts
applies. Defaults5
.:max_children
- the maximum amount of children to be running under this supervisor at the same time. When:max_children
is exceeded,start_child/2
returns{:error, :max_children}
. Defaults to:infinity
.:extra_arguments
- arguments prepended to the child spec given tostart_child/2
application.ex
children=[
....,
{Registry, keys: :unique, name: Registry.EventFolderGenServer},
ReactPhoenix.Dynamic.EventFolderSupervisor,
Start Child
Start genservers/process dynamically
Supervisor won't restart if init fails, so use send self() for init
Way 1 Child Spec
DynamicSupervisor.start_child(MySupervisor, Stack)
DynamicSupervisor.start_child(MySupervisor, {Stack, [:hello]})
use GenServer
, agent, or supervisor all define default child_spec as below
def child_spec(arg) do
%{id: Stack, start: {Stack, :start_link, [arg]}
end
Can modify child_spec options directly in the use
use GenServer, restart: transient
Way 2 Direct Spec
spec = %{
id: PriceChecker,
start: {PriceChecker, :start_link, [2]}
}
DynamicSupervisor.start_child(MySupervisor, spec)
:id
- any term to identify the child specification internally (Required):start
- a tuple with the module-function-args to be invoked to start the child process (Required):restart
:permanent
- always restarted(Default):temporary
- never restarted:transient
- restarted only if terminates abnormally (anything besides:normal
,:shutdown
, or{:shutdown, term}
)
:shutdown
- how to shutdown, defaults to5_000
if the type is:worker
or:infinity
if the type is:supervisor
.:type
-:worker
(default) or a:supervisor
Example
worker.ex
defmodule ReactPhoenix.Dynamic.Recording do
use GenServer
def start_link(event_id) do
name = {:via, Registry, {EventGenServer, "#{event_id}_recording"}}
# wrap in :ok as start_link expects it
case GenServer.start_link(__MODULE__, event_id, name: name) do
{:ok, pid} -> {:ok, pid}
{:error, {:already_started, pid}} -> {:ok, pid}
x -> x
end
end
def init(state) do
send(self(), :work)
{:ok, state}
end
def handle_info(:work, state) do
# do important stuff
IO.puts("#{state} Important stuff in progress...")
if state <= 20 do
Process.send_after(self(), :work, 1000)
{:noreply, state + 1}
else
#will restart if restart strategy :permanent
{:stop, :normal, state}
end
end
end
supervisor.ex
defmodule ReactPhoenix.Dynamic.Supervisor do
use DynamicSupervisor
alias ReactPhoenix.Dynamic.Recording
def start_link(_arg) do
DynamicSupervisor.start_link(__MODULE__, :ok, name: __MODULE__)
end
def init(:ok) do
DynamicSupervisor.init(strategy: :one_for_one)
end
def start_recording(event_id) do
spec = %{id: Recording, start: {Recording, :start_link, [event_id]}}
DynamicSupervisor.start_child(__MODULE__, spec)
end
end
application.ex
children = [
ReactPhoenix.PriceSupervisor,
Usage, test in console:
ReactPhoenix.PriceSupervisor.start_work
Last updated