Elixir Applications and Children
An application is a started program whose root is a supervisor built from a list of children. Starting the application starts that supervisor, and the supervisor starts every child in the list.
Starting the application starts its children
The start function builds the root supervisor from two children. Both workers answer.
defmodule Worker do
use GenServer
def start_link({name, initial}), do: GenServer.start_link(__MODULE__, initial, name: name)
def init(initial), do: {:ok, initial}
def handle_call(:get, _from, state), do: {:reply, state, state}
def handle_cast(:inc, state), do: {:noreply, state + 1}
end
defmodule CounterApp.Application do
use Application
def start(_type, _args) do
children = [
%{id: :a, start: {Worker, :start_link, [{:a, 10}]}},
%{id: :b, start: {Worker, :start_link, [{:b, 20}]}}
]
Supervisor.start_link(children, strategy: :one_for_one)
end
end
{:ok, sup} = CounterApp.Application.start(:normal, [])
IO.inspect(GenServer.call(:a, :get))
IO.inspect(GenServer.call(:b, :get))
Supervisor.stop(sup)
The root supervisor restarts a failed child
Both children are raised by one. Then child a fails, and only a starts again from 10.
defmodule Worker do
use GenServer
def start_link({name, initial}), do: GenServer.start_link(__MODULE__, initial, name: name)
def init(initial), do: {:ok, initial}
def handle_call(:get, _from, state), do: {:reply, state, state}
def handle_cast(:inc, state), do: {:noreply, state + 1}
end
defmodule CounterApp.Application do
use Application
def start(_type, _args) do
children = [
%{id: :a, start: {Worker, :start_link, [{:a, 10}]}},
%{id: :b, start: {Worker, :start_link, [{:b, 20}]}}
]
Supervisor.start_link(children, strategy: :one_for_one)
end
end
{:ok, sup} = CounterApp.Application.start(:normal, [])
GenServer.cast(:a, :inc)
GenServer.cast(:b, :inc)
IO.inspect(GenServer.call(:a, :get))
IO.inspect(GenServer.call(:b, :get))
Process.exit(GenServer.whereis(:a), :kill)
Process.sleep(100)
Supervisor.which_children(sup)
IO.inspect(GenServer.call(:a, :get))
IO.inspect(GenServer.call(:b, :get))
Supervisor.stop(sup)
Stopping the root ends the application workers
The root supervisor is stopped at the end of this block. Both workers are gone with it.
defmodule Worker do
use GenServer
def start_link({name, initial}), do: GenServer.start_link(__MODULE__, initial, name: name)
def init(initial), do: {:ok, initial}
def handle_call(:get, _from, state), do: {:reply, state, state}
def handle_cast(:inc, state), do: {:noreply, state + 1}
end
defmodule CounterApp.Application do
use Application
def start(_type, _args) do
children = [
%{id: :a, start: {Worker, :start_link, [{:a, 10}]}},
%{id: :b, start: {Worker, :start_link, [{:b, 20}]}}
]
Supervisor.start_link(children, strategy: :one_for_one)
end
end
{:ok, sup} = CounterApp.Application.start(:normal, [])
Supervisor.stop(sup)
IO.inspect(GenServer.whereis(:a) == nil)
IO.inspect(GenServer.whereis(:b) == nil)
Example
defmodule Worker do
use GenServer
def start_link({name, initial}), do: GenServer.start_link(__MODULE__, initial, name: name)
def init(initial), do: {:ok, initial}
def handle_call(:get, _from, state), do: {:reply, state, state}
def handle_cast(:inc, state), do: {:noreply, state + 1}
end
defmodule CounterApp.Application do
use Application
def start(_type, _args) do
children = [
%{id: :a, start: {Worker, :start_link, [{:a, 10}]}},
%{id: :b, start: {Worker, :start_link, [{:b, 20}]}}
]
Supervisor.start_link(children, strategy: :one_for_one)
end
end
{:ok, sup} = CounterApp.Application.start(:normal, [])
GenServer.cast(:a, :inc)
GenServer.cast(:b, :inc)
IO.inspect(GenServer.call(:a, :get))
IO.inspect(GenServer.call(:b, :get))
Process.exit(GenServer.whereis(:b), :kill)
Process.sleep(100)
Supervisor.which_children(sup)
IO.inspect(GenServer.call(:a, :get))
IO.inspect(GenServer.call(:b, :get))
Supervisor.stop(sup)
Key points
- Application: a program unit that boots through one start function.
- start: the function that returns the root supervisor for the application.
- Children list: the list of child specifications handed to the root supervisor.
- Root supervisor: the supervisor at the top of the application, started directly by start.
- Direct call: in a script the start function is called directly with the arguments normal and an empty list.
In this example, starting the application returns its root supervisor, and both children answer after being raised to 11 and 21. When child b fails, the root supervisor restarts only b. After the failure, a still answers 11 and b answers 20 again. The root supervisor is stopped at the end.
Note
the application module uses the Application behaviour, so its start function has the shape the runtime expects.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
What does an application start function return?