phx_leave message. This is handled by the LiveView process here, conveying to OTP to shut down the process normally.
On ‘abnormal’ exits such as violently killing the browser program through the OS’ task manager, the computer losing power, the internet connection being lost, etc., this message will not be sent. Depending on the situation, this will either result in a socket which is closed, or a socket which is (for the time being) hanging, i.e. seemingly open but not responding to any requests. Recognizing which processes are hanging vs. which are ‘truly’ alive is what the heartbeat mechanism does. In either case (‘dead’ or ‘hanging’), at the ‘Phoenix Channel’-layer the socket will end up being closed, which the LiveView-process will see and handle here as another type of incoming message.
In either way, these messages will be handled by the LiveView GenServer only after the
handle_*-action for the current message has finished executing. So if that action takes multiple seconds to execute, the process will be alive for those couple of extra seconds.
This seems like everything might be all right. However, there is one situation in which the LiveView process will be killed before finishing executing their current action: when the parent process, the OTP application, or more commonly your full Elixir program is stopped or is restarted. Depending on how important the action is, you might want to handle this situation properly. A common approach is to use Oban or other persistent-job-processing libraries, which will shorten your LiveView action and ensure that the calculation+e-mail sending happens atomically and still goes through if the app was restarted in the meantime.
Another advantage of using that technique is that you don’t lock a user out of interacting with the LiveView UI while the action is being executed, which might be a problem if it takes longer than a couple of seconds.