Repository: phoenixframework/phoenix

Issue #2432: Channels cannot be hot reloaded
Created: 2017-08-04T10:19:59Z
Environment

Elixir version (elixir -v): Erlang/OTP 20 [erts-9.0] Elixir 1.6.0-dev (e4455a411)
Phoenix version (mix deps): master

Expected behavior
A channel is able to be upgraded as any process in the OTP application.
Actual behavior
Channels are not automatically picked up for upgrades by the standard OTP mechanism. This is because the channel processes are not started by the supervisors, but instead by the transport processes. This means that the discovery mechanism has no way of reaching the channels processes to know to update them.
I'm not sure if it's possible for the transport process to start behaving like a supervisor to make it possible or if it would be required to introduce real supervisors. If it's deemed the fix would be too complex, it should be at least documented that channels don't work with standard upgrade processes.

Duplicate issue #2466: invoking `:init.stop` takes 5 seconds if at least one websocket client is connected
Created: 2017-08-31T09:56:28Z
Environment

Elixir version (elixir -v): 1.5.0
Phoenix version (mix deps): 1.3.0

Expected behavior
Invoking :init.stop should properly terminate idle socket process as soon as possible.
Actual behavior
If at least one client is connected over websocket, invoking :init.stop takes 5 seconds until it kills the socket process brutally:
mix phx.new test --no-ecto
cd test
iex -S mix phx.server

# open a browser and navigate to localhost:4000

iex> :init.stop
# the system stops after 5 seconds

If no websocket client is connected, the system stops almost immediately.
I didn't test the behaviour with long polling.
More details
I believe the issue originates here. The socket process is trapping exits, so by default it won't terminate when it receives a shutdown signal from its parent.
When handling the :EXIT message, the socket process only reacts to an exit of a child. Hence the exit sent from the parent is ignored, and the process does nothing. It will ultimately be terminated after the timeout happens in the parent, but we're waiting for 5 seconds for no real purpose.
At surface, the solution would be to detect if the :EXIT process originated from the parent, and if so, issue the same exit signal to children (channel processes), wait for them to terminate, or kill them brutally if they refuse to do so (e.g. they are trapping exits).
One problem is that it seems there's no information in the socket process about the parent. I believe this is due to how cowboy (or more precisely ranch) starts socket processes. This could be solved with some trickery, e.g. if the cowboy handler used Process.info(self(), [:links]) during init phase to determine the parent (the only process which is linked). It's a hack, but it should do the job.
One thing which would be really nice is to allow channels to send some final info before they terminate. I think this might turn out to be quite tricky so maybe that should be tackled separately.

Closing comment (2017-08-31T13:55:13Z): Related to #2432 , in that if we properly start and supervise  channel processes, then this issue is mostly fixed, I think.