I am trying to set up a supervision tree for a scheduler application (note using Elixir 1.5 syntax). The application should work so that:
start_child
callback. This takes the initialisation args that are used to build the schedule state.If I don't pass any arguments, I can get this working - the Schedules created are not registered, and I have to modify the state after creation. As soon as I try to add arguments, the system errors out - I know it's just a syntax misunderstanding on my part, but I cannot for the life of me figure out what I'm doing wrong. I haven't found the docs terribly helpful here, and I've tried copying and modifying examples from GH, GH Gists and from articles online, but I cannot get it to work.
Current setup - ideally I would want to pass id
, period
and targets
as arguments to start_child
, but can't even get it working with a single argument, so just sticking with the one until I can get it running:
The Application:
defmodule Assay.Application do
use Application
def start(_type, _args) do
children = [
{Assay.SchedulerSupervisor, []},
{Registry, keys: :unique, name: Assay.Scheduler.Registry}
]
opts = [strategy: :one_for_all, name: Assay.Supervisor]
Supervisor.start_link(children, opts)
end
end
The Supervisor:
defmodule Assay.SchedulerSupervisor do
use Supervisor
@name Assay.SchedulerSupervisor
def start_link(_args) do
Supervisor.start_link(__MODULE__, :ok, name: @name)
end
def start_schedule(id) do
Supervisor.start_child(@name, [id])
end
def init(_) do
Supervisor.init([Assay.Scheduler], [strategy: :simple_one_for_one, name: @name])
end
end
The GenServer (only relevant initialisation functions shown)
defmodule Assay.Scheduler do
use GenServer
alias Assay.Scheduler
require Logger
defstruct targets: [], period: 60_000, id: nil, active: false
def start_link(id) do
GenServer.start_link(__MODULE__, [id], [name: via_tuple(id)])
end
def init([id]) do
Logger.info "starting a new #{__MODULE__} with id #{id}"
{:ok, %Scheduler{id: id}}
end
end
edit: actual error might help - I can see that the args are wrong, I just can't figure out why:
{:error,
{:EXIT,
{:undef,
[{Assay.Scheduler, :start_link, [[], 1], []},
{:supervisor, :do_start_child_i, 3, [file: 'supervisor.erl', line: 381]},
{:supervisor, :handle_call, 3, [file: 'supervisor.erl', line: 406]},
{:gen_server, :try_handle_call, 4, [file: 'gen_server.erl', line: 636]},
{:gen_server, :handle_msg, 6, [file: 'gen_server.erl', line: 665]},
{:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 247]}]}}}
For :simple_one_for_one
supervisors, Supervisor.start_child
calls the start function with the arguments given to it with the arguments specified in the Child Specification. When using Supervisor.init
, the child specification is taken from the module's child_spec/1
function in Elixir 1.5. Since you're using GenServer
and not specifying a custom start function and []
is passed to child_spec/1
, this defaults to [[]]
which means your function ends up being called with two arguments, []
and 1
if the id
is 1
and you get an undefined function error.
You can fix this by explicitly saying you don't want the GenServer to provide any arguments to the start function in child_spec
by changing
use GenServer
to
use GenServer, start: {__MODULE__, :start_link, []}
Now the function will be called correctly, with only one argument, which will be the id
.
IO.inspect Assay.SchedulerSupervisor.start_link []
IO.inspect Assay.SchedulerSupervisor.start_schedule 12
will print:
{:ok, #PID<0.82.0>}
[info] starting a new Elixir.Assay.Scheduler with id 12
{:ok, #PID<0.83.0>}