Skip to content
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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

Conversation

kim
Copy link
Contributor

@kim kim commented Feb 26, 2025

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.

@kim kim requested review from gefjon and coolreader18 February 26, 2025 08:43
"Error reading message from read WebSocket stream",
Err(e),
);
break;
Copy link
Contributor Author

@kim kim Feb 26, 2025

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.
@kim kim force-pushed the kim/sdk/client-ping branch from 081ce29 to abda910 Compare February 26, 2025 08:50
record_metrics(other.len());
},
},

_ = idle_timeout_interval.tick() => {
if mem::replace(&mut idle, true) {
if mem::take(&mut want_pong) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we mem::takeing here? It looks to me like this is equivalent to just reading want_pong in all cases.

  • want_pong == true: take and replace with false. Break out of the loop and never read from want_pong again.
  • want_pong == false: take is a no-op. Set want_pong to true later in this block. Never read the false value installed by mem::take.

It seems like there are two possible simplifications to this code:

  • mem::replace(&mut want_pong, true) here, and remove the want_pong = true assignment on line 390.
  • Just branch on want_pong with if want_pong { here, and leave the assignment later on.

Comment on lines +386 to +390
Self::maybe_log_error(
"Error sending ping",
self.sock.send(WebSocketMessage::Ping(vec![])).await,
);
want_pong = true;
Copy link
Contributor

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants