By Sergey Gernyak, Back-end Engineer.
Mostly when you create your new Elixir application you’ll run the following command:
mix new my_app --sup
It generates a blank project with a root supervisor. For example:
defmodule SomeApp do
use Application
import Supervisor.Spec
def start(_type, _args) do
import Supervisor.Spec, warn: false
children = [
# worker(DynamicProcesses.Worker, [arg1, arg2, arg3]),
]
opts = [strategy: :one_for_one, name: SomeApp.Supervisor]
Supervisor.start_link(children, opts)
end
end
In the children array you can specify a list of workers (or supervisors) that will be supervised by the root supervisor.
All those settings are static. But you’re also able do that in runtime. In order to do this you’ll need several functions from the Elixir’s core: Supervisor.start_child/2, Supervisor.Spec.supervisor/3 and Supervisor.Spec.worker/3. Let’s go through some examples. All of these examples are taken from this project on github.
The algorithm is very simple:
To create the specification Supervisor.Spec.supervisor/3 function is used. Let’s take a look at the example:
defmodule DynamicProcesses do
use Application
def start(_type, _args) do
import Supervisor.Spec, warn: false
children = [
]
opts = [strategy: :one_for_one, name: DynamicProcesses.Supervisor]
Supervisor.start_link(children, opts)
end
end
defmodule DynamicProcesses.Examples do
alias DynamicProcesses.SomeSupervisor
import Supervisor.Spec
def add_single_supervisor(id \\ "1") do
{:ok, supervisor_spec} = build_supervisor_spec(SomeSupervisor, [], id)
Supervisor.start_child(DynamicProcesses.Supervisor, supervisor_spec)
end
defp build_supervisor_spec(module, args, id) do
supervisor_spec = supervisor(module, args, [id: "supervisor" <> id])
{:ok, supervisor_spec}
end
end
defmodule DynamicProcesses.SomeSupervisor do
use Supervisor
def start_link do
Supervisor.start_link(__MODULE__, [])
end
def init([]) do
children = []
supervise(children, strategy: :one_for_all)
end
end
After running DynamicProcesses.Example.add_single_supevisor you will get a new supervisor which is supervised by the application root one.
Run observer by running :observer.start in the iex session.
Double click on the <0.151.0> (it could be different in your case) and check that it is the right process (I mean that the right module is its base ).
The algorithm for starting supervisors is the same however, for building specifications a different function is used. Lets consider an example:
defmodule DynamicProcesses.Examples do
alias DynamicProcesses.{SomeSupervisor, SomeWorker}
import Supervisor.Spec
def add_single_supervisor(id \\ "1") do
{:ok, supervisor_spec} = build_supervisor_spec(SomeSupervisor, [], id)
Supervisor.start_child(DynamicProcesses.Supervisor, supervisor_spec)
end
def add_supervisor_with_workers do
{:ok, supervisor_pid} = add_single_supervisor
{:ok, worker_spec1} = build_worker_spec(SomeWorker, [], "1")
{:ok, worker_spec2} = build_worker_spec(SomeWorker, [], "2")
Supervisor.start_child(supervisor_pid, worker_spec1)
Supervisor.start_child(supervisor_pid, worker_spec2)
end
defp build_supervisor_spec(module, args, id) do
supervisor_spec = supervisor(module, args, [id: "supervisor" <> id])
{:ok, supervisor_spec}
end
defp build_worker_spec(module, args, id) do
worker_spec = worker(module, args, [id: "worker" <> id])
{:ok, worker_spec}
end
end
defmodule DynamicProcesses.SomeWorker do
use GenServer
def start_link do
GenServer.start_link(__MODULE__, %{})
end
end
Take a look at DynamicProcesses.Examples.add_supervisor_with_workers/0 function. Here we do the following:
Here <0.114.0> is our new supervisor, <0.115.0> and <0.116.0> are its child workers.
As I understand it, by default you can only create one single process for a module. Perhaps it’s because Elixir uses the name of a module as a process identifier.
And that’s all folks! Thanks for reading!
An executive’s guide to AI and Intelligent Automation. Working Machines takes a look at how the renewed vigour for the development of Artificial Intelligence and Intelligent Automation technology has begun to change how businesses operate.