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 and init/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 and arg

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 the Supervisor module docs.

  • :max_restarts - the maximum number of restarts allowed in a time frame. Defaults to 3.

  • :max_seconds - the time frame in which :max_restarts applies. Defaults to 5.

  • :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 to start_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