A behaviour module for implementing supervision functionality.
A supervisor is a process which supervises other processes called child processes. Supervisors are used to build an hierarchical process structure called a supervision tree, a nice way to structure fault-tolerant applications.
A supervisor implemented using this module will have a standard set of interface functions and include functionality for tracing and error reporting. It will also fit into an supervision tree.
Example
In order to define a supervisor, we need to first define a child process that is going to be supervised. In order to do so, we will define a GenServer that represents a stack:
defmodule Stack do
use GenServer
def start_link(state) do
GenServer.start_link(__MODULE__, state, [name: :sup_stack])
end
def handle_call(:pop, _from, [h|t]) do
{:reply, h, t}
end
def handle_cast({:push, h}, t) do
{:noreply, [h|t]}
end
end
We can now define our supervisor and start it as follows:
# Import helpers for defining supervisors
import Supervisor.Spec
# We are going to supervise the Stack server which will
# be started with a single argument [:hello]
children = [
worker(Stack, [[:hello]])
]
# Start the supervisor with our one child
{:ok, pid} = Supervisor.start_link(children, strategy: :one_for_one)
Notice that when starting the GenServer, we have registered it
with name :sup_stack
, which allows us to call it directly and
get what is on the stack:
GenServer.call(:sup_stack, :pop)
#=> :hello
GenServer.cast(:sup_stack, {:push, :world})
#=> :ok
GenServer.call(:sup_stack, :pop)
#=> :world
However, there is a bug in our stack server. If we call :pop
and
the stack is empty, it is going to crash because no clause matches.
Let's try it:
GenServer.call(:sup_stack, :pop)
=ERROR REPORT====
Luckily, since the server is being supervised by a supervisor, the
supervisor will automatically start a new one, with the default stack
of [:hello]
like before:
GenServer.call(:sup_stack, :pop) == :hello
Supervisors support different strategies; in the example above, we
have chosen :one_for_one
. Furthermore, each supervisor can have many
workers and supervisors as children, each of them with their specific
configuration, shutdown values, and restart strategies.
Continue reading this moduledoc to learn more about supervision strategies
and then follow to the Supervisor.Spec
module documentation to learn
about the specification for workers and supervisors.
Module-based supervisors
In the example above, a supervisor was dynamically created by passing
the supervision structure to start_link/2
. However, supervisors
can also be created by explicitly defining a supervision module:
defmodule MyApp.Supervisor do
use Supervisor
def start_link do
Supervisor.start_link(__MODULE__, [])
end
def init([]) do
children = [
worker(Stack, [[:hello]])
]
supervise(children, strategy: :one_for_one)
end
end
You may want to use a module-based supervisor if:
You need to do some particular action on supervisor initialization, like setting up a ETS table.
You want to perform partial hot-code swapping of the tree. For example, if you add or remove a children, the module-based supervision will add and remove the new children directly, while the dynamic supervision requires the whole tree to be restarted in order to perform such swaps.
Strategies
:one_for_one
- if a child process terminates, only that process is restarted.:one_for_all
- if a child process terminates, all other child processes are terminated and then all child processes (including the terminated one) are restarted.:rest_for_one
- if a child process terminates, the "rest" of the child processes, i.e. the child processes after the terminated one in start order, are terminated. Then the terminated child process and the rest of the child processes are restarted.:simple_one_for_one
- similar to:one_for_one
but suits better when dynamically attaching children. This strategy requires the supervisor specification to contain only one children. Many functions in this module behave slightly differently when this strategy is used.
Name Registration
A supervisor is bound to the same name registration rules as a GenServer
.
Read more about it in the GenServer
docs.