DynamicSupervisor behaviour
A supervisor that starts children dynamically.
The Supervisor
module was designed to handle mostly static children that are started in the given order when the supervisor starts. A DynamicSupervisor
starts with no children. Instead, children are started on demand via start_child/2
. When a dynamic supervisor terminates, all children are shutdown at the same time, with no guarantee of ordering.
Examples
A dynamic supervisor is started with no children, often under a supervisor with the supervision strategy (the only strategy currently supported is :one_for_one
) and a name:
children = [ {DynamicSupervisor, strategy: :one_for_one, name: MyApp.DynamicSupervisor} ] Supervisor.start_link(strategy: :one_for_one)
The options given in the child specification are documented in start_link/1
.
Once the dynamic supervisor is running, we can start children with start_child/2
, which receives a child specification:
{:ok, agent1} = DynamicSupervisor.start_child(MyApp.DynamicSupervisor, {Agent, fn -> %{} end}) Agent.update(agent1, &Map.put(&1, :key, "value")) Agent.get(agent1, & &1) #=> %{key: "value"} {:ok, agent2} = DynamicSupervisor.start_child(MyApp.DynamicSupervisor, {Agent, fn -> %{} end}) Agent.get(agent2, & &1) #=> %{} DynamicSupervisor.count_children(sup) #=> %{active: 2, specs: 2, supervisors: 0, workers: 2}
Module-based supervisors
Similar to Supervisor
, dynamic supervisors also support module-based supervisors.
defmodule MyApp.DynamicSupervisor do # Automatically defines child_spec/1 use DynamicSupervisor def start_link(arg) do DynamicSupervisor.start_link(__MODULE__, arg, name: __MODULE__) end @impl true def init(_arg) do DynamicSupervisor.init(strategy: :one_for_one) end end
See the Supervisor
docs for a discussion of when you may want to use module-based supervisors.
Name registration
A supervisor is bound to the same name registration rules as a GenServer
. Read more about these rules in the documentation for GenServer
.
Migrating from Supervisor’s :simple_one_for_one
In case you were using the deprecated :simple_one_for_one
strategy from the Supervisor
module, you can migrate to the DynamicSupervisor
in few steps.
Imagine the given “old” code:
defmodule MySupervisor do use Supervisor def start_link(arg) do Supervisor.start_link(__MODULE__, arg, name: __MODULE__) end def start_child(foo, bar, baz) do # This will start child by calling MyWorker.start_link(initial_arg, foo, bar, baz) Supervisor.start_child(__MODULE__, [foo, bar, baz]) end @impl true def init(initial_arg) do children = [ # Or the deprecated: worker(MyWorker, [initial_arg]) %{id: MyWorker, start: {MyWorker, :start_link, [initial_arg]}) ] Supervisor.init(children, strategy: :simple_one_for_one) end end
It can be upgraded to the DynamicSupervisor like this:
defmodule MySupervisor do use DynamicSupervisor def start_link(arg) do DynamicSupervisor.start_link(__MODULE__, arg, name: __MODULE__) end def start_child(foo, bar, baz) do # If MyWorker is not using the new child specs, we need to pass a map: # spec = %{id: MyWorker, start: {MyWorker, :start_link, [foo, bar, baz]}} spec = {MyWorker, foo: foo, bar: bar, baz: baz} DynamicSupervisor.start_child(__MODULE__, spec) end @impl true def init(initial_arg) do DynamicSupervisor.init( strategy: :one_for_one, extra_arguments: [initial_arg] ) end end
The difference is that the DynamicSupervisor
expects the child specification at the moment start_child/2
is called, and no longer on the init callback. If there are any initial arguments given on initialization, such as [initial_arg]
, it can be given in the :extra_arguments
flag on DynamicSupervisor.init/1
.
Summary
Types
- init_option()
-
Options given to
start_link/2
andinit/1
- on_start_child()
-
Return values of
start_child
functions - option()
-
Option values used by the
start*
functions - options()
-
Options used by the
start*
functions - strategy()
-
Supported strategies
- sup_flags()
-
The supervisor flags returned on init
Functions
- child_spec(arg)
-
Returns a specification to start a dynamic supervisor under a supervisor
- count_children(supervisor)
-
Returns a map containing count values for the supervisor
- init(options)
-
Receives a set of options that initializes a dynamic supervisor
- start_child(supervisor, child_spec)
-
Dynamically adds a child specification to
supervisor
and starts that child - start_link(options)
-
Starts a supervisor with the given options
- start_link(mod, args, opts \\ [])
-
Starts a module-based supervisor process with the given
module
andarg
- terminate_child(supervisor, pid)
-
Terminates the given child identified by child id
- which_children(supervisor)
-
Returns a list with information about all children
Callbacks
- init(args)
-
Callback invoked to start the supervisor and during hot code upgrades
Types
init_option()
init_option() :: {:strategy, strategy()} | {:max_restarts, non_neg_integer()} | {:max_seconds, pos_integer()} | {:max_children, non_neg_integer() | :infinity} | {:extra_arguments, [term()]}
Options given to start_link/2
and init/1
on_start_child()
on_start_child() :: {:ok, pid()} | {:ok, pid(), info :: term()} | :ignore | {:error, {:already_started, pid()} | :max_children | term()}
Return values of start_child
functions
option()
option() :: {:name, Supervisor.name()} | init_option()
Option values used by the start*
functions
options()
options() :: [option(), ...]
Options used by the start*
functions
strategy()
strategy() :: :one_for_one
Supported strategies
sup_flags()
sup_flags() :: %{ strategy: strategy(), intensity: non_neg_integer(), period: pos_integer(), max_children: non_neg_integer() | :infinity, extra_arguments: [term()] }
The supervisor flags returned on init
Functions
child_spec(arg)
Returns a specification to start a dynamic supervisor under a supervisor.
See Supervisor
.
count_children(supervisor)
count_children(Supervisor.supervisor()) :: %{ specs: non_neg_integer(), active: non_neg_integer(), supervisors: non_neg_integer(), workers: non_neg_integer() }
Returns a map containing count values for the supervisor.
The map contains the following keys:
-
:specs
- the number of children processes -
:active
- the count of all actively running child processes managed by this supervisor -
:supervisors
- the count of all supervisors whether or not the child process is still alive -
:workers
- the count of all workers, whether or not the child process is still alive
init(options)
init([init_option()]) :: {:ok, map()}
Receives a set of options that initializes a dynamic supervisor.
This is typically invoked at the end of the init/1
callback of module-based supervisors. See the sections “Module-based supervisors” in the module documentation for more information.
The options received by this function are also supported by start_link/2
.
This function returns a tuple containing the supervisor options.
Examples
def init(_arg) do DynamicSupervisor.init(max_children: 1000, strategy: :one_for_one) end
Options
-
:strategy
- the restart strategy option. The only supported value is:one_for_one
which means that no other child is terminate if a child process terminates. You can learn more about strategies in theSupervisor
module docs. -
:max_restarts
- the maximum number of restarts allowed in a time frame. Defaults to3
. -
:max_seconds
- the time frame in which:max_restarts
applies. Defaults to5
. -
: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, :dynamic}
. Defaults to:infinity
. -
:extra_arguments
- arguments that are prepended to the arguments specified in the child spec given tostart_child/2
. Defaults to an empty list.
start_child(supervisor, child_spec)
start_child( Supervisor.supervisor(), :supervisor.child_spec() | {module(), term()} | module() ) :: on_start_child()
Dynamically adds a child specification to supervisor
and starts that child.
child_spec
should be a valid child specification. The child process will be started as defined in the child specification.
If the child process start function returns {:ok, child}
or {:ok, child,
info}
, then child specification and PID are added to the supervisor and this function returns the same value.
If the child process start function returns :ignore
, then no child is added to the supervision tree and this function returns :ignore
too.
If the child process start function returns an error tuple or an erroneous value, or if it fails, the child specification is discarded and this function returns {:error, error}
where error
is a term containing information about the error and child specification.
If the supervisor already has N children in a way that N exceeds the amount of :max_children
set on the supervisor initialization (see init/1
), then this function returns {:error, :max_children}
.
start_link(options)
start_link(options()) :: Supervisor.on_start()
Starts a supervisor with the given options.
The :strategy
is a required option and the currently supported value is :one_for_one
. The remaining options can be found in the init/1
docs.
The :name
option can also be used to register a supervisor name. The supported values are described under the “Name registration” section in the GenServer
module docs.
If the supervisor is successfully spawned, this function returns {:ok, pid}
, where pid
is the PID of the supervisor. If the supervisor is given a name and a process with the specified name already exists, the function returns {:error, {:already_started, pid}}
, where pid
is the PID of that process.
Note that a supervisor started with this function is linked to the parent process and exits not only on crashes but also if the parent process exits with :normal
reason.
start_link(mod, args, opts \\ [])
start_link(module(), term(), GenServer.options()) :: Supervisor.on_start()
Starts a module-based supervisor process with the given module
and arg
.
To start the supervisor, the init/1
callback will be invoked in the given module
, with arg
as its argument. The init/1
callback must return a supervisor specification which can be created with the help of the init/1
function.
If the init/1
callback returns :ignore
, this function returns :ignore
as well and the supervisor terminates with reason :normal
. If it fails or returns an incorrect value, this function returns {:error, term}
where term
is a term with information about the error, and the supervisor terminates with reason term
.
The :name
option can also be given in order to register a supervisor name, the supported values are described in the “Name registration” section in the GenServer
module docs.
terminate_child(supervisor, pid)
terminate_child(Supervisor.supervisor(), pid()) :: :ok | {:error, :not_found}
Terminates the given child identified by child id.
If successful, this function returns :ok
. If there is no process with the given PID, this function returns {:error, :not_found}
.
which_children(supervisor)
which_children(Supervisor.supervisor()) :: [ {:undefined, pid() | :restarting, :worker | :supervisor, :supervisor.modules()} ]
Returns a list with information about all children.
Note that calling this function when supervising a large number of children under low memory conditions can cause an out of memory exception.
This function returns a list of tuples containing:
-
id
- it is always:undefined
for dynamic supervisors -
child
- the pid of the corresponding child process or the atom:restarting
if the process is about to be restarted -
type
-:worker
or:supervisor
as defined in the child specification -
modules
- as defined in the child specification
Callbacks
init(args)
init(args :: term()) :: {:ok, sup_flags()} | :ignore
Callback invoked to start the supervisor and during hot code upgrades.
Developers typically invoke DynamicSupervisor.init/1
at the end of their init callback to return the proper supervision flags.
© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.6.6/DynamicSupervisor.html