All,
In a LiveView
process mount callback I’m spawn linking a conversational bot process:
@impl true
def mount(_params, _session, socket) do
{:ok, conversation_pid} = Conversation.start_link()
socket = assign(socket, :conversation_pid, conversation_pid)
{:ok, socket}
end
Conversation
relevant code is:
@spec start_link() :: GenServer.on_start()
def start_link() do
GenServer.start_link(__MODULE__, [])
end
def init([]) do
Logger.info("Starting conversation")
_ = Process.flag(:trap_exit, true)
{:ok, %{}}
end
@impl true
def handle_info({:EXIT, _pid, _reason}, state) do
Logger.warning("ON DIED!")
{:stop, :normal, state}
end
@impl true
def terminate(_reason, _state) do
Logger.warn("DIED!")
end
When I open a browser on /chat
I get:
[info] GET /chat
[debug] Processing with LysaWeb.ChatLive.Elixir.LysaWeb.ChatLive/2
Parameters: %{}
Pipelines: [:browser]
[info] Starting conversation
[info] Sent 200 in 1ms
[warning] DIED!
[info] CONNECTED TO Phoenix.LiveView.Socket in 18µs
Transport: :websocket
Serializer: Phoenix.Socket.V2.JSONSerializer
Parameters: %{"_csrf_token" => "PX0APQUODhlUHX9ZEQkKLTIJIzsnURd7U8nLStCxyo3jfGobTqnQcdpC", "_live_referer" => "undefined", "_mounts" => "0", "_track_static" => %{"0" => "http://localhost:4000/assets/app.css", "1" => "http://localhost:4000/assets/app.js"}, "vsn" => "2.0.0"}
[debug] MOUNT LysaWeb.ChatLive
Parameters: %{}
Session: %{"_csrf_token" => "hEnqVzMa-rL3wNeOfxMjD5g8"}
[info] Starting conversation
[debug] Replied in 49µs
You can see that the conversation gets started once, then dies, then is started again (to be expected given that mount is called twice).
When I close the browser tab, I get:
[warning] DIED!
==> QUESTION: what I would have expected is for the ON DIED!
log message to be displayed as well, given that we are trapping exits: a {:EXIT, #PID<0.113.0>, :normal}
message should be received by the Conversation
process, but it’s not. Why is that?
Now, if I don’t trap exits:
@impl true
def init([]) do
Logger.info("Starting conversation")
# _ = Process.flag(:trap_exit, true)
{:ok, %State{}}
end
When I open /chat
:
[info] GET /chat
[debug] Processing with LysaWeb.ChatLive.Elixir.LysaWeb.ChatLive/2
Parameters: %{}
Pipelines: [:browser]
[info] Starting conversation
[info] Sent 200 in 12ms
[info] CONNECTED TO Phoenix.LiveView.Socket in 14µs
Transport: :websocket
Serializer: Phoenix.Socket.V2.JSONSerializer
Parameters: %{"_csrf_token" => "IDI8STU7fFNAJnthASc3OSlJBTAjRwJyHwR8cA12mT7RviRvO1HZgreJ", "_live_referer" => "undefined", "_mounts" => "0", "_track_static" => %{"0" => "http://localhost:4000/assets/app.css", "1" => "http://localhost:4000/assets/app.js"}, "vsn" => "2.0.0"}
[debug] MOUNT LysaWeb.ChatLive
Parameters: %{}
Session: %{"_csrf_token" => "hEnqVzMa-rL3wNeOfxMjD5g8"}
[info] Starting conversation
[debug] Replied in 62µs
When I close the browser tab, I don’t get anything.
This means that the Conversation
process is never killed, despite it being spawn linked. This could be expected behaviour, given that the exit reason of the LiveView
process is probably :normal
.
Let’s then see what happens when we blow up the LiveView
process on mount:
@impl true
def mount(_params, _session, socket) do
{:ok, conversation_pid} = Conversation.start_link()
1/0
end
I get:
[info] GET /chat
[debug] Processing with LysaWeb.ChatLive.Elixir.LysaWeb.ChatLive/2
Parameters: %{}
Pipelines: [:browser]
[info] Starting conversation
[info] Sent 500 in 33ms
[error] #PID<0.625.0> running Phoenix.Endpoint.SyncCodeReloadPlug (connection #PID<0.623.0>, stream id 1) terminated
Server: localhost:4000 (http)
Request: GET /chat
** (exit) an exception was raised:
** (ArithmeticError) bad argument in arithmetic expression
So yet again the Conversation
process isn’t killed, even though the linked LiveView
process exited with a non normal reason.
==> QUESTION: why is the Conversation
process, which was spawn linked, not killed?
Can some kind soul clarify these for me?
Thank you in advance.