Skip to content

Commit

Permalink
More examples and rationale behind child_spec/1
Browse files Browse the repository at this point in the history
  • Loading branch information
José Valim committed Dec 4, 2017
1 parent db85115 commit a2a1f3d
Showing 1 changed file with 83 additions and 60 deletions.
143 changes: 83 additions & 60 deletions lib/elixir/lib/supervisor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -99,65 +99,6 @@ defmodule Supervisor do
The rest of this document will cover how child processes are started,
how they can be specified, different supervision strategies and more.
## child_spec/1
When starting a supervisor, we pass a list of child specifications.
Those specifications are maps that tell how the supervisor should
start, stop and restart each of its children:
%{
id: Stack,
start: {Stack, :start_link, [[:hello]]}
}
The map above defines a supervisor with `:id` of `Stack` that is started
by calling `Stack.start_link([:hello])`. There are other options we can
set in the specification and we will explore those in the next sections.
For now, however, we want to clarify that specifying the child specification
for each child as a map is repetitive and error prone. That's why Elixir
allows you to simply pass the module name or a tuple with the module name
and the `start_link` argument instead of the child specification:
children = [
{Stack, [:hello]}
]
In this case, the supervisor will invoke `Stack.child_spec([:hello])` which
then returns the child specification. It is also possible to simply pass the
`Stack` module as a child:
children = [
Stack # equivalent to {Stack, []}
]
The advantage of this approach is that you can now share your `Stack` service
with other developers and they can add it directly to their supervision tree
without worrying about the low-level details of the service.
In case you need to access or modify how a service runs, you can use the
`child_spec/2` function defined in this module. To run the stack with a different
`:id`:
children = [
Supervisor.child_spec({Stack, [:hello]}, id: MyStack)
]
We can see the child specification the call above returns:
Supervisor.child_spec({Stack, [:hello]}, id: MyStack)
#=> %{id: MyStack, start: {Stack, :start_link, [[:hello]]}}
The `child_spec/1` function was automatically defined in our `Stack` module
by `use GenServer`. You can also configure the GenServer to use a different
`:id` or to use a `:shutdown` value of 10 seconds (10_000 milliseconds) when
the `Stack` is defined:
use GenServer, id: MyStack
use GenServer, shutdown: 10_000
Let's learn more about how supervisors start and shutdown.
## Start and shutdown
When the supervisor starts, it traverses all children and retrieves
Expand Down Expand Up @@ -278,7 +219,89 @@ defmodule Supervisor do
`:normal`, `:shutdown` or `{:shutdown, term}`.
For a more complete understanding of the exit reasons and their
impact, see the "Exit reasons and restarts" section next.
impact, see the "Exit reasons and restarts" section.
## child_spec/1
When starting a supervisor, we pass a list of child specifications. Those
specifications are maps that tell how the supervisor should start, stop and
restart each of its children:
%{
id: Stack,
start: {Stack, :start_link, [[:hello]]}
}
The map above defines a supervisor with `:id` of `Stack` that is started
by calling `Stack.start_link([:hello])`.
However, specifying the child specification for each child as a map can be
quite error prone, as we may change the Stack implementation and forget to
update its specification. That's why Elixir allows you to pass a tuple with
the module name and the `start_link` argument instead of the specification:
children = [
{Stack, [:hello]}
]
The supervisor will then invoke `Stack.child_spec([:hello])` to retrieve a
child specification. Now the `Stack` module is responsible for building its
own specification. By default, `use GenServer` defines a `Stack.child_spec/1`
function which returns the same child specification we had before:
%{
id: Stack,
start: {Stack, :start_link, [[:hello]]}
}
It is also possible to simply pass the `Stack` module as a child:
children = [
Stack
]
When only the module name is given, it is equivalent to `{Stack, []}`. In this
case, we will end-up with a child specification that looks like this:
%{
id: Stack,
start: {Stack, :start_link, [[]]}
}
By replacing the map specification by `{Stack, [:hello]}` or `Stack`, we keep
the child specification encapsulated in the Stack module, using the default
implementation defined by `use GenServer`. We can now share our `Stack` worker
with other developers and they can add it directly to their supervision tree
without worrying about the low-level details of the worker.
If you need to access or modify how a worker or a supervisor runs, you can use
the `Supervisor.child_spec/2` function. For example, to run the stack with a
different `:id` and a `:shutdown` value of 10 seconds (10_000 milliseconds):
children = [
Supervisor.child_spec({Stack, [:hello]}, id: MyStack, shutdown: 10_000)
]
The call to `Supervisor.child_spec/2` above will return the following specification:
%{
id: MyStack,
start: {Stack, :start_link, [[:hello]]},
shutdown: 10_000
}
You may also configure the child specification in the Stack module itself to
use a different `:id` or `:shutdown` value by passing options to `use GenServer`:
defmodule Stack do
use GenServer, id: MyStack, shutdown: 10_000
The options above will customize the `Stack.child_spec/1` function defined
by `use GenServer`. It accepts the same options as the `Supervisor.child_spec/2`
function.
You may also completely override the `child_spec/1` function in the Stack module
and return your own child specification.
## Exit reasons and restarts
Expand Down

0 comments on commit a2a1f3d

Please sign in to comment.