I need to know whether the client is online, when WiFi is down.
The server should know it immediately. Can anyone tell me an elegant solution?
Now as I tested, Phoenix Channel needs about 40-50s to detect the client is disconnected.
I need to know whether the client is online, when WiFi is down.
The server should know it immediately. Can anyone tell me an elegant solution?
Now as I tested, Phoenix Channel needs about 40-50s to detect the client is disconnected.
Welcome @WalkPhoneGo. The server should instantly know when a client is no longer connected, if the disconnection was clean. If it was not clean and stayed open on the server side, it would take a heartbeat cycle for it to realize that the connection is no longer present. Phoenix Channels are processes and will die when the parent Socket handler dies, which happens when the connection is severed.
Iâve only really seen clean disconnects in the wild. I did see phantom connections once, but it was on a load test when the execution server did not kill its client connections properly.
An instant event in this case is technically impossible.
Only the client knows about the cut down WiFi. But since WiFi is down, it canât tell the server.
There might even be situations in which neither of the participants know about the disconnect. Lets say your ISP drops the connection. Neither your client nor your server will know this until they try the next heartbeat and it times out.
This is just how networking works.
Thanks, that means I have to implement a PING-PONG mechanism to continuously check whether the client is offline.
Since it already has a ping from the server, I just need to implement pong from client to server. Am I right?
tks, looks I have to make it by myselfâŠ
While it cannot be âinstantaneousâ, it definitely doesnât need to be a full 40-50 seconds to detect an offline state from the server.
This shouldnât be necessary since Phoenix Channelâs have heartbeat support built-in, and you can tweak the interval.
From the phoenix js docs: phoenix 1.7.10 | Documentation you want to tweak heartbeatIntervalMs
. Although in the normal case of someone closing the tab that should be detected without waiting for the heartbeat.
You make me confused, it means Phoenix channel client will send heartbeats to the server in default.
If that so, why server need 40-50s to know the client is offline due to the WiFi down?
The connection isnât severing cleanly in this case. One side of the connection thinks it is online (server) while the other side (client) is certainly not online. Many load balancers / proxies will kill connections if they donât send data in so many seconds. The heartbeat process normally works 2 fold: it keeps the connection alive in your load balancer, and it lets the Phoenix server (Cowboy) know that the client is still connected.
The duration of this heartbeat defaults to 30s (https://github.com/phoenixframework/phoenix/blob/b9a7582b726a210a86e8d55153bc426294890cb2/assets/js/phoenix.js#L753). You can change this to a lower number if you want to know about a disconnection sooner. However, that will come at the cost of additional messages / processing / bandwidth. Itâs probably fine for a smaller application.
I change the default value to 3s, but the server still needs 40-50s to detect client offline.
I think the server-side have to handle the heartbeat. And sadly I donât know how to do it.
I found this from phoenix source code https://github.com/phoenixframework/phoenix/blob/f3fe0188aaa53bfa10d7750cffe5008d211756bf/lib/phoenix/socket.ex#L585
so how can I handle this in my code?
You need to also set the webserverâs idle timeout to a lower value, ie
config :app, AppWeb.Endpoint,
http: [
...,
protocol_options: [
idle_timeout: 10_000
]
]
Yes!!!, thatâs what I need. work like a charmïŒïŒïŒïŒïŒïŒ
Thank you!
I was fed up with the Actionable so I chose Phoenix Channel,itâs like magic!!!
I have tried this but it does not work. It takes Phoenix up to 60 seconds to detect if a user is disconnected. The only way to make it detect faster is by:
socket("/socket", MemeWeb.UserSocket,
websocket: [
timeout: 10000
],
longpoll: false
)
But this makes sockets reconnect every 10 seconds.
Did you update the client heartbeat interval? phoenix/assets/js/phoenix.js at b9a7582b726a210a86e8d55153bc426294890cb2 · phoenixframework/phoenix · GitHub
Yes I did. Like this:
let channel = socket.channel("room:clients", {
heartbeatIntervalMs: 5000
})
Move this configuration to the new Socket("url", { heartbeatIntervalMs: 5000 })
call. The option is per-connection (Socket) and not per-Channel.
It didnât make a difference. There is some time of up to 60 seconds between network disconnection and the removal from the Presence list.
Do you have a sample project that can reproduce this? I would take a look if you did.
Hey @acrolink, I got your example figured out (feel free to post the example here, it may be useful for others?)
In the code example you sent, the idle_timeout was set at the cowboy HTTP config level. Run :ranch.get_protocol_options(MemeWeb.Endpoint.HTTP)
to see what I mean. By default (no protocol options set), you wonât see idle_timeout
in the HTTP endpoint. If you specify the idle timeout, then youâll see it from that command.
However, cowboy_websocket
uses a separate mechanism than the HTTP idle_timeout for it. You can see the option specified at https://ninenines.eu/docs/en/cowboy/2.2/manual/cowboy_websocket/. Phoenix sets up the cowboy_websocket
opts at this line of code, and you have to specify it in the format timeout: X
.
In your app, that is done with:
socket("/socket", MemeWeb.UserSocket,
websocket: [
timeout: 10_000
]
)
This code was commented out in the repo you sent me. I added it back in, set it to 10k, and I reliably detected failure within 10 seconds using your reproduction steps.
tl;dr the timeout
option must be set in the socket
options, and the UI must specify the heartbeatIntervalMs to a time lower than that. A value of 10_000 (socket) and 5_000 (client) will detect failure within 10 seconds.
This indeed is the solution. I was aware of the socket timeout
parameter but was placing the heartbeatIntervalMs
in the wrong place. Thank you very much @sb8244.