-
Notifications
You must be signed in to change notification settings - Fork 109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
sdk: Client ping on idle connections #2309
base: master
Are you sure you want to change the base?
Conversation
"Error reading message from read WebSocket stream", | ||
Err(e), | ||
); | ||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
None of the other error variants indicate a particularly recoverable situation, so I added this break
.
Make the websocket loop send a `Ping` frame when the connection is idle for a while. "idle" includes missing server-side pings. Writing to the socket will detect if the remote end went away, but FIN / RST went missing for some reason. This is not detected by only reading.
081ce29
to
abda910
Compare
record_metrics(other.len()); | ||
}, | ||
}, | ||
|
||
_ = idle_timeout_interval.tick() => { | ||
if mem::replace(&mut idle, true) { | ||
if mem::take(&mut want_pong) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we mem::take
ing here? It looks to me like this is equivalent to just reading want_pong
in all cases.
want_pong == true
:take
and replace withfalse
. Break out of the loop and never read fromwant_pong
again.want_pong == false
:take
is a no-op. Setwant_pong
totrue
later in this block. Never read thefalse
value installed bymem::take
.
It seems like there are two possible simplifications to this code:
mem::replace(&mut want_pong, true)
here, and remove thewant_pong = true
assignment on line 390.- Just branch on
want_pong
withif want_pong {
here, and leave the assignment later on.
Self::maybe_log_error( | ||
"Error sending ping", | ||
self.sock.send(WebSocketMessage::Ping(vec![])).await, | ||
); | ||
want_pong = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to break the loop if we fail to send a ping?
Make the websocket loop send a
Ping
frame when the connection is idle for a while. "idle" includes missing server-side pings.Writing to the socket will detect if the remote end went away, but FIN / RST went missing for some reason. This is not detected by only reading.
I haven't touched the scary server-side websocket loop, but we may consider to also make it send pings only if it doesn't have anything else to send.
Expected complexity level and risk
2
Testing
The situation can be approximated by inducing a 100% packet loss scenario.
This can be done in various ways. A convenient one is
docker compose disconnect
, which disables the virtual interface of a container.Without this patch, you'll see the server timing out the connection after a while (as it did not receive pong), while the client keeps thinking that something may eventually arrive.
With this patch, both ends eventually close the connection.