Oliver

Oliver

Supervisor dies with its child?

I have an application with the following setup:

  • There is a central supervisor which is initialized with a child spec at startup encompassing three permanent children. It is started with start_link/2 in the application’s start/2.
  • During the run of the application I start a child supervisor by calleing the central supervisor with start_child/2 and Supervisor.Spec.supervisor/3. This one is started as transient.
  • The child supervisor starts a set of children with transient and one_for_all.

The intent behind that is that each group of workers is tied together and is dismissed after its common task is done. The child supervisor is supposed to restart children on errors only but to let them go gracefully when their job is done. (A “normal” exit.)

The central supervisor has a name (alias MyMod.Supervisor) it was started with. When I query it with Supervisor.which_children(MyMod.Supervisor) it shows me the child supervisor is there.

Now:

  • Whenever I try to use Process.exit/2 to end the child supervisor nothing happens.
  • When I use Supervisor.terminate_child/2 with the assigned child ID in the central supervisor, it returns ok but the next call to Supervisor.which_children(MyMod.Supervisor) tells me there is no process (the central supervisor died)
  • When I use Supervisor.stop/2 in the child with its registered name and :normal I also can afterwards not query the central supervisor (again, it died).

It seems like no matter what I do, if the child exits, the central supervisor exits. I’ve played around with the Supervisor.Spec I created it with. Made no difference.

Any ideas?

Marked As Solved

josevalim

josevalim

Creator of Elixir

Quick tip: you should investigate if this is related to the controlling process: gen_tcp — OTP 29.0.2 (kernel 11.0.2)

Also Liked

NobbZ

NobbZ

Can you provide a minimal example project which does show your problem? Only from your wording, I can roughly understand the problem you have, but without seeing what you are really doing, one can’t tell what you are doing wrong…

Oliver

Oliver

Hmmm… tried to do that, but my sample project seems to work:

[code]defmodule Test do

import Supervisor.Spec

def start do
# static children
children = [
worker(Worker, [0], restart: :permanent, id: :static1),
worker(Worker, [1], restart: :permanent, id: :static2),
worker(Worker, [2], restart: :permanent, id: :static3)
]

Supervisor.start_link(children, strategy: :one_for_one, name: Central.Supervisor)
    Supervisor.start_child(Central.Supervisor,
                           supervisor(Worker.Supervisor, [3], restart: :transient, id: :super))
    test

end

def test do
IO.puts “Central: #{inspect Supervisor.which_children(Central.Supervisor)}”
IO.puts “Worker: #{inspect Supervisor.which_children(Worker.Supervisor)}”

# Supervisor.terminate_child(Central.Supervisor, :super)
    Worker.Supervisor.stop
    IO.puts "Central: #{inspect Supervisor.which_children(Central.Supervisor)}"

end

end

defmodule Worker.Supervisor do

use Supervisor

def start_link(id) do
Supervisor.start_link(MODULE, [id], name: Worker.Supervisor)
end

def init([id]) do
children = [
worker(Worker, [id], restart: :transient, id: 0),
worker(Worker, [id+1], restart: :transient, id: 1),
worker(Worker, [id+2], restart: :transient, id: 2)
]

supervise(children, strategy: :one_for_all)

end

def stop do
Supervisor.stop(Worker.Supervisor)
end

end

defmodule Worker do

use GenServer

def start_link id do
GenServer.start_link(MODULE, , name: {:global, {:worker, id}})
end

end[/code]

Oliver

Oliver

Now I have SASL reports for my app and the following transpires:

2016-04-3011:59:51.496[ERR/connector.ex:43] Received error when listening on TCP port, reason was :closed 2016-04-3011:59:51.496[ERR/SASL] GenServer {:connector, 1} terminating | ** (stop) :error 2016-04-3011:59:51.498[ERR/SASL] Process #PID<0.235.0> terminating ** (exit) :error (stdlib) gen_server.erl:826: :gen_server.terminate/7 (stdlib) proc_lib.erl:240: :proc_lib.init_p_do_apply/3 Initial Call: Connector.init/1 Ancestors: [#PID<0.233.0>, MyMod.Supervisor, #PID<0.181.0>] 2016-04-3011:59:51.498[ERR/SASL] Child {:connection, 1} of Supervisor {:supervisor, 1} shutdown abnormally | ** (exit) :error | Pid: #PID<0.235.0> | Start Call: Connector.start_link(1, #Port<0.7095>) 2016-04-3011:59:51.498[INF/SASL] Application myapp exited: normal

In my application startup I create a TCP listener port:

{ :ok, listenSocket } = :gen_tcp.listen(readUeTcpPort(), [:binary, active: true, packet: :raw, reuseaddr: true])

I then give the listenSocket to each worker child I start:

def addWorker(workerId, listenSocket) do Supervisor.start_child(MyMod.Supervisor, supervisor(Worker.Supervisor, [workerId, listenSocket], restart: :transient, id: {:worker, workerId}, shutdown: :infinity)) end

These workers run their init/1 and send themselves a GenServer.call that makes them wait on :gen_tcp.accept. The first worker is dynamically created during the run of my application’s start/2.

Whenever a new client is accepted, the worker immediately calls MyMod.addWorker to start the next process waiting on the listen port.

Now, when I stop the supervisor of the first group of workers, something odd happens. The 2nd worker suddenly drops out of its :gen_tcp.accept call it is sleeping on with an error. Apparently the listenSocket is somehow closed. And somehow this leads to my whole application to quietly shut down with exit :normal.

Any ideas?

Last Post!

david_ex

david_ex

Then I’d suggest reading my blog series: it should require minimal modifications for what you want to do. Of course, if want you really want to have is a constant pool of workers, you’ll probably want to look at something like poolboy instead.

Where Next?

Popular in Questions Top

Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
PeterCarter
There are pre-rolled solutions for other frameworks that do work. However, Phoenix does not seem to have these. Have people had good expe...
New
komlanvi
Hi everyone, I was playing with phoenix liveView but I run into an issue. I have a form and want to validate each input text when the te...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New

Other popular topics Top

KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36820 110
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" =&gt; #BSON.ObjectId&lt;58eb1a7a9ad169198c3dXXXX&gt;, "email" =&gt; ...
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New

We're in Beta

About us Mission Statement