Hi,
I’m using Phoenix.Socket.Transport to accept websocket connections. For each websocket connection, I need to start a client process that connects to a different service. This process will remain up and connected for the duration of the websocket connection. When either of the two processes crashes, the other process needs to crash as well (there is no way to recover from this). When the websocket closes normally, the client process needs to be stopped.
When I run this:
defmodule Stack do
use GenServer
@impl true
def init(stack) do
{:ok, stack}
end
end
spid = spawn(fn ->
{:ok, pid} = GenServer.start_link(Stack, [:hello])
IO.puts("gs: #{inspect pid}")
end)
# Process.alive?(pid( gs pid ))
the genserver stay alive, even though the spawned process immediately exits. When I trap exits in the Genserver, the genserver goes down with the spawned process, which is what I want:
defmodule Stack do
use GenServer
@impl true
def init(stack) do
Process.flag(:trap_exit, true)
{:ok, stack}
end
end
spid = spawn(fn ->
{:ok, pid} = GenServer.start_link(Stack, [:hello])
IO.puts("gs: #{inspect pid}")
end)
# Process.alive?(pid( gs pid ))
It seems to me this is a clean solution because I can ensure from the client code, that it will always go down, even if I forget to explicitly shut down the genserver from a terminate callback. This is also expected from the docs:
If trap_exit is set to false, the process exits if it receives an exit signal other than normal and the exit signal is propagated to its linked processes.
https://erlang.org/~lukas/predefined-types/erts-12.1.2/doc/html/erlang.html#process_flag-2
However, Erlang Slack says I should use the terminate callback instead, because trapping exits “has some additional risks with it if the code doesn’t handle all cases correctly”. I’m not sure what those risks are.
Crashes
Furthermore, on https://elixir-lang.org/getting-started/mix-otp/genserver.html#monitors-or-links it says:
If you link two processes and one of them crashes, the other side will crash too (unless it is trapping exits).
However, if I make my spawn process crash:
defmodule Stack do
use GenServer
@impl true
def init(stack) do
Process.flag(:trap_exit, true)
{:ok, stack}
end
end
spid = spawn(fn ->
{:ok, pid} = GenServer.start_link(Stack, [:hello])
IO.puts("gs: #{inspect pid}")
raise "bye"
end)
both the Genserver and the spawned process crash, even though the docs say “the other side will crash too, unless it is trapping exits”.
So my questions are:
- Is it appropriate to use start_link from the websocket process?
- Should I trap exits in the client connection, or should I stop the client from the websocket terminate callback?
- If I should not trap exits, why?
- Is the documentation page I linked about missing something about crashing and trapping exits?