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
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
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
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
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?
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








