I’ve got an app that connects to a number of TCP sockets using Ranch.
The supervision tree looks like this:
|-------|
| APP |
|-------|
|
▼
|---------------------|
| Manager(Supervisor) |
|---------------------|
| | |
▼ ▼ ▼
|-------||-------||----------------|
| Sup 1 || Sup 2 || Sup N |
|-------||-------||----------------|
| |
▼ ▼
|------------| |-------------|
| TCP Reader | | Other Child |
|------------| |-------------|
For each connection I start a GenServer in a supervisor… The GenServer init\1
calls connect\1
.
def connect(config) do
case :ranch_tcp.connect(config.ip, config.port, []) do
{:ok, socket} ->
{:ok, socket}
error = {:error, :econnrefused} ->
Logger.error("Connection refused to #{inspect(config)}. Shutting down Reader")
error
error = {:error, _error} ->
Logger.error(
"CTC Socket failed to connect to #{inspect(config)}. Shutting down Reader"
)
error
end
end
What I want to do is let the Supervisor and its children die gracefully if :ranch_tcp.connect\2
returns {:error, :econnrefused}
The GenServer init looks like this
@impl true
def init(config) do
case connect(config) do
{:ok, socket} ->
#nice, start reading
{:ok, %{:config => config, socket: socket}}
{:error, :econnrefused} ->
#too bad. Close self and Supervisor gracefully
exit(:normal)
{:error, error} ->
#What the what! Try again X times
{:stop, error}
end
end
Observed behaviour
If the Genserver init\1
does not return {:ok, state}
the error is propagated all the way to the Application, and it quits.
Starting ctc socket app
12:25:25.694 [error] Connection refused to %Server{ctc_name: :down_tcp_server, ip: {127, 0, 0, 1}, port: 5555, recv_buffer: 0}. Shutting down CtcReader
12:25:25.697 [info] Application ctc_socket exited: CtcSocket.Application.start(:normal, []) returned an error: shutdown: failed to start child: Supervision.Manager
** (EXIT) shutdown: failed to start child: :down_tcp_server_supervisor
** (EXIT) shutdown: failed to start child: :down_tcp_server_reader
** (EXIT) normal
** (Mix) Could not start application ctc_socket: CtcSocket.Application.start(:normal, []) returned an error: shutdown: failed to start child: Supervision.Manager
** (EXIT) shutdown: failed to start child: :down_tcp_server_supervisor
** (EXIT) shutdown: failed to start child: :down_tcp_server_reader
** (EXIT) normal