Hello all…
I’ve run into a problem that I can’t figure out and I was hoping to get a second set of eyes on. I imagine it’s something silly that I’m just missing due to confirmation biases or similar.
I’m trying to start a GenServer under a dynamic supervisor and when I call DynamicSupervisor.start_child/2
using a child spec, I end up with an :invalid_child_spec
error. Naturally I assume something is wrong with my child spec… but I can’t see it. Let me provide some details and what I’ve steps I’ve already taken. To start some versions: Elixir 1.16.2 with Erlang 26.2.3.
The child spec and GenServer start is part of a larger function which does some other setup/startup stuff based on options passed to the function. The child spec is generated using the following line:
settings_service_spec =
%{
id: opts[:childspec_id],
start:
{MscmpSystSettings, :start_link,
[opts[:service_name], opts[:datastore_context_name], []]}
}
Which in turn produces a child spec that looks like:
%{
id: MscmpSystSettings.Runtime.DevSupport,
start: {MscmpSystSettings, :start_link,
[
:"MscmpSystSettings.TestSupportService",
{:via, Registry,
{MscmpSystSettings.TestRegistry, "0000mt.tstspt.MscmpSystDb.ContextRole"}},
[]
]}
}
Eventually this ends up at DynamicSupervisor.start_child/2
:
DynamicSupervisor.start_child(resolved_supervisor_name, settings_service_spec)
…and it’s the start_child/2
call that ends with an :invalid_child_spec
error:
{:invalid_child_spec,
{{MscmpSystSettings, :start_link,
[
:"MscmpSystSettings.TestSupportService",
{:via, Registry,
{MscmpSystSettings.TestRegistry, "0000mt.tstspt.MscmpSystDb.ContextRole"}},
[]
]}, :permanent, 5000, :worker, [MscmpSystSettings]}}}
As far as I can tell, the child spec is structured correctly… I have an :id
value, and a valid :start
mfa tuple.
So part of what I’ve done is trace how this code runs through DynamicSupervisor
source. I can see what specific function in the source is producing the error as it appears to be the only way to get an :invalid_child_spec
, but what I can’t see is how the error message spec is even possible.
After a few steps in Supervisor
which really don’t look like they do anything for the story here, we end up at DynamicSupervisor.validate_child/1
:
defp validate_child(%{id: _, start: {mod, _, _} = start} = child) do
restart = Map.get(child, :restart, :permanent)
type = Map.get(child, :type, :worker)
modules = Map.get(child, :modules, [mod])
significant = Map.get(child, :significant, false)
shutdown =
case type do
:worker -> Map.get(child, :shutdown, 5_000)
:supervisor -> Map.get(child, :shutdown, :infinity)
end
validate_child(start, restart, shutdown, type, modules, significant)
end
Presumably this is were the values I didn’t provide in the submitted child spec, but appear in the error message, came from… though interestingly the significant
value doesn’t appear in the error message version of the spec. This function ends with a call to validate_child/6
. However, the error message reported child spec didn’t have 6 values it only had 5 and the error message apparently comes from validate_child/1
when no prior match happens:
defp validate_child(other) do
{:invalid_child_spec, other}
end
Which wouldn’t respond to a call to validate_child/6
.
And ultimately this is where my confusion steps in… I can’t see how I could get far enough along to get values I didn’t provide defaulted into the child spec given in the error message… but end up at that error message. Sure, there could be lots of other failure modes, but I can’t see where I’d be taking any different path than the one I described.
Again, I have to be missing something simple and otherwise obvious in the spec, the path through Supervisor
and DynamicSupervisor
that the code takes, etc.
Thanks in advance,
Steve