sbuttgereit

sbuttgereit

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

Showing Posts 1 to 10

derek-zhou

derek-zhou

Really? Where is the :id in the error message?

sbuttgereit

sbuttgereit OP

If those other values I didn’t add really are coming from the validate_child/1 call matching the map version… I wouldn’t expect to see :id. The signature of that function is:

validate_child(%{id: _, start: {mod, _, _} = start} = child)

The :id value is explicitly ignored and I wouldn’t expect to see it in the error. In the child spec that I’m passing, the :id value is there.

But this is the issue. In reality I expect that I’m not hitting that validate_child/1 function which matches on the child spec map… but somewhere I’m getting most of the extra values which aren’t in my child spec defaulted in and that’s the only place I see where it might come from … so its as though I’m hitting it… I just don’t see how that’s possible.

Interestingly, the error child spec is given as a tuple, not a map. So I’m digging right now to see how that might be possibly happening. My sense is that maybe I’m not finding all the places where :invalid_child_spec may be coming from.

sbuttgereit

sbuttgereit OP

My apologies… this is a silly statement on my part considering the match on child is what is used in the meat of that function… but in the end it doesn’t do anything with :id anywhere anyway… it doesn’t pass it on into the subsequent calls.

derek-zhou

derek-zhou

Look at the back trace. You are not passing the right child_spec and the function balked. Add logging at the right place if necessary.

LostKobrakai

LostKobrakai

I’d suggest tracing the functions involved and see if the correct values reach the places you expect them to reach.

al2o3cr

al2o3cr

This is peculiar - it looks like the result of validate_child/6 has somehow ended up as the input of validate_child/1 :thinking:

I don’t understand how this could be happening - the only place that calls validate_child/1 is start_child, and that’s guarded to only allow in 6-tuples or maps. Passing a 5-tuple to start_child would give an ArgumentError in Supervisor.child_spec/2 before it even made it to validate_child.

sbuttgereit

sbuttgereit OP

Right. I assume that’s the case. But I can’t see how that original child spec is invalid. My looking through the Elixir code was an effort to figure out what I’m doing wrong in the child spec… but I can’t line up what the child spec is with the result that Elixir is producing.

Good tip. I know that my understanding of Elixir/Erlang debugging, tracing, and monitoring is underdeveloped… and this is very helpful. I wasn’t aware of this tool and I think it could be what I need to at least.

Naturally, I couldn’t understand it either… but the extrace tip from @LostKobrakai is getting me going in the right direction. What I’ve found so far is that the child spec I’m submitting appears to actually be passing the validate_child/1 (map match) and validate_child/6 successfully. I don’t appear to actually be getting to:

defp validate_child(other) do
  {:invalid_child_spec, other}
end

The error is coming from somewhere else.

The reason we see a 5 tuple is because that’s what validate_child/6 produces:

defp validate_child(start, restart, shutdown, type, modules, significant) do
    with :ok <- validate_start(start),
         :ok <- validate_restart(restart),
         :ok <- validate_shutdown(shutdown),
         :ok <- validate_type(type),
         :ok <- validate_modules(modules),
         :ok <- validate_significant(significant) do
      {:ok, {start, restart, shutdown, type, modules}}
    end
  end

This ultimately gets passed to DynamicSupervisor.call/2 which in turn calls GenServer.call/3. So in the trace I see:

GenServer.call(:"MscmpSystSettings.TestSupportSupervisor", {:start_child,
 {{MscmpSystSettings, :start_link,
   [
     :"MscmpSystSettings.TestSupportService",
     {:via, Registry,
      {MscmpSystSettings.TestRegistry, "0000mt.tstspt.MscmpSystDb.ContextRole"}},
     []
   ]}, :permanent, 5000, :worker, [MscmpSystSettings]}}, :infinity)

… and that’s as far as I’ve gotten. They key point is that, at least to this point I’m passing the child spec validation step enough for the GenServer.call/3 attempt.

I’m not done troubleshooting it yet, but at this point I’m not stuck.

dimitarvp

dimitarvp

You already had good answers and they sadly have not helped – IMO the next logical step is to make a reproducible example in a singular file that’s using Mix.install?

al2o3cr

al2o3cr

The other place that :invalid_child_spec could come from is in :supervisor.handle_call’s :start_child clause:

https://github.com/erlang/otp/blob/64fcd877c7645cea51e0b532255cc602f6e9aad5/lib/stdlib/src/supervisor.erl#L998-L1005

The only way I could see this happening is calling DynamicSupervisor.start_child with a name or PID that’s actually running :supervisor code.

sbuttgereit

sbuttgereit OP

Quite the contrary. I agree I’ve gotten good answers… and they have helped me on this problem immensely. Enough so that I think I can solve the problem without additional help. This is why I mentioned I’m no longer stuck.

Benjamin’s advice regarding tracing and pointing me to that extrace utility was sufficient to break the mental block that I couldn’t get past: I assumed my child spec had to be wrong and the place where it had to fail was in the validate_child functions of DynamicSupervisor: extrace showed me that my child spec was not failing were I was stubbornly believing it must be; the ability to trace through Elixir private functions has made the most difference. While I haven’t found the problem yet, that’s now more about my personal stamina to pursue it further today than missing the knowledge to figure it out… I’m quite sure I’ve got what I need now to solve the problem at this point.

And while Matt’s answer didn’t directly get me on the right path… he at least gave me some comfort that I might not be a complete moron in my reading of the issue. Matt’s answers in this community are regular and of consistently high quality… that his reading was similar to mine gave me hope that I was being somewhat reasonable.

You’re no slouch either :slight_smile: … but this time I have to both agree and disagree with you.

If I get to the root cause, I’ll post what I find here for the record in case it might help others. I have some suspicions about what might be wrong, but I’ll wait until I’ve completed the troubleshooting.

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
water
I’m aware of 2 such projects, are there others? git: GitHub - team-alembic/realworld: A fullstack Phoenix LiveView application with back...
New
itzmidinesh
Hi all, I’m working on a Phoenix project where I need to make external HTTP requests and I’m trying to choose between Req and HTTPoison....
New
peck
Hi everyone. My team and I have been working on a fairly modest app based around video streaming and chat, but we’ve landed a customer t...
New
leoschweizer
Hi, we run different LLM models across a cluster, with each node loading a configurable subset. Model-specific queues let nodes consume ...
New
csmr
Hi all, I’m new here and would like to hear peoples views on my Elixir/Phoenix based game-engine! :smiley: Mutonex gamedesign was finish...
New
Stef-Verniers
Hello I’ve been coding in Elixir for almost two years now because of my current job where we implemented Elixir in our application. But ...
New

Other Trending Topics Top

dominicletz
Hey there, I haven’t seen a list of useful Elixir specific skills for the popular AI tools yet so I’m starting one here with my first. ...
New
raa-ziq
Hi everyone :waving_hand: I wanted to share something I’ve been building — NowBlind, a random chat app designed to help people discover ...
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
OndrejValenta
(I just needed to vent somewhere and LinkedIn is full of hope, or hype, I’m not sure which exactly) AI dream has many faces, but general...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews