eidge
DynamicSupervisor starting child with more than one argument
Hey there guys,
I’m trying to use a DynamicSupervisor to start multiple children, however I need to register them with a specific name. However from the documentation there seems to be no way of doing this, unless I pass a map to the start_child/2 function:
DynamicSupervisor.start_child(pid, {Pipeline, job_module})
What would be the best way of starting and registering this child with a name?
Most Liked
josevalim
The second element of the tuple can be a keyword list, where you would pass multiple options:
{Pipeline, job_module: job_module, name: ...}
In case the Pipeline module does not support the new child specs, then you need to build a child specification by hand:
%{
id: Pipeline,
start: {Pipeline, :start_link, [job_module, [name: ...]]
}
peerreynders
I interpreted his advice to go with the fully featured map-based version of the spec:
%{
id: Pipeline,
start: {Pipeline, :start_link, [job_module, [name: ...]]
}
rather than the tuple short form, not necessarily modifying start_link.
GenServer (which it is) start_link/2
FYI: actually it’s start_link/3
start_link(module, args, options \\ [])
Ultimately it’s this line
start: {Pipeline, :start_link, [job_module, [name: ...]]
that determines how the child process is started. Pipeline is simply the module that owns the startin function, :start_link identifies the function to “start” the process (it could be set :fred - and then you better provide a fred function) and the list that follows are the arguments to be used when calling start_link (or fred).
So there really is no default signature - the tuple spec assumes a name of start_link but that is a mere convention but there is no fixed arity.
Ultimately the function is run with something like Kernel.apply/3 - so the number of elements in the args list ties into the function’s arity - keeping in mind that the last element can be keyword list to mimic variadic arguments (I personally find it confusing when the enclosing brackets are left out “for convenience”).
Just to crudely demonstrate:
defmodule Pipeline do
use Supervisor
def init(args) do
{:ok, args}
end
defp queue_name(job_module),
do: String.to_atom("#{job_module}-queue")
def spec_map(job_module),
do: %{
id: Pipeline,
start: {
Pipeline,
:fake_start_link,
[job_module, [name: queue_name(job_module)]]
}
}
def fake_start_link(job_module, opts) do
IO.puts("job_module: #{inspect job_module}");
IO.puts("opts: #{inspect opts}")
// GenServer.start_link(job_module, [], opts)
end
end
job_module = Queue;
spec = Supervisor.child_spec(Pipeline, Pipeline.spec_map(job_module))
IO.inspect spec
{m,f,a} = spec.start
Kernel.apply(m,f,a)
$ elixir demo.exs
%{
id: Pipeline,
start: {Pipeline, :fake_start_link, [Queue, [name: :"Elixir.Queue-queue"]]},
type: :supervisor
}
job_module: Queue
opts: [name: :"Elixir.Queue-queue"]
josevalim
I don’t think that will happen because you always need to implement the GenServer callbacks and that’s when you will wrap its start_link/3 by your own start_link/1. I would never expect somebody to start a {GenServer, …} directly in a supervision tree.







