Closed zheilbron closed 1 year ago
I did indeed leave the disconnection/close behavior into a quick and dirty grey territory until now. It's good that you looked into it and it made me took a closer look too. Here are my thoughts about it:
Desired behaviors :
ConnectionLostEvent
ConnectionLostEvent
ConnectionLostEvent
We are still missing a few bits so I proposed additional changes to your PR to fulfill those:
ConnectionLostEvent
should not be emitted if the connection closed due to a call to client::close_connection => change in udate_sync_client
to only emit the event if the state was not already Disconnected
ClientLostConnection
should not be emitted if the client connection ended following a call to disconnect_client => change in update_sync_server
to only emit the event if the client was not already disconnectedDisconnected
=> check for Disconnected
state in connection methods, and add a new Connecting
state as the initialization state.to_server_receiver
queue before flushing => send LostConnection SyncMessage from send_msg
to_client_receiver
queue before flushing => send ClientLostConnection SyncMessage from send_msg
Thanks a lot for your work ! Feel free to review the additional changes I propose. I'll wait for your opinion to merge those.
Minors:
info!("Connection closed: {}", close_reason);
since "normally" closing locally of by the peer is not really considered as an "error".send()
with non flushing feed()
or send_all()
is on the roadmap, we might as well leave the flush()
calls.Possible future tweaks:
close_waiter
and send_msg
, and simply calling connection.disconnect()
from update_sync_client
. This could introduce a delay of up to 1 frame to start closing the async tasks, which is not a time critical part. But it would simplify a bit the async part by not having to ever use the close_sender
in it, which is finicky since calling it might stop the calling task itself right after.Thanks for taking a holistic look at the issue and fixing up the PR. I agree with the desired behaviors.
client: In close_waiter, we could rename conn_err to close_reason and log as: info!("Connection closed: {}", close_reason); since "normally" closing locally of by the peer is not really considered as an "error".
I agree. This is how quinn names it though: https://docs.rs/quinn/latest/quinn/struct.Connection.html#method.closed
I think that we don't need to call flush() yet, since send() does a flush every time. But since replacing send() with non flushing feed() or send_all() is on the roadmap, we might as well leave the flush() calls.
I was debating this, but couldn't convince myself whether or not it was needed. Thanks for pointing out that send
does indeed flush.
One question I'm still unsure about:
What is the lifecycle of a connection, particularly one that has reached the Disconnected
state? Can it ever be reconnected or is it essentially garbage at that point? And, if it's garbage, should it be collected automatically once it's no longer needed?
I noticed that this is problematic if one relies on the default
connection. If the default connection ever becomes disconnected, it remains a defunct connection until manually cleared with client::close_connection
.
As of now, connections are indeed kind of a dead weight once Disconnected
. This is partly why I made connection::disconnect private, and only used through the public client::close_connection which properly dispose of the connection.
But you are right that even so we can't close them ourselves, connections can still become Disconnected
due to other external factors (errors, server closing, ...) which is an annoyance when it happens to the default connection.
I was thinking that I might add a reconnect
to re-use the same ConnectionConfiguration and ConnectionId. It seems to offer better ergonomics than a new open_connection call, allowing for thinks like "auto-reconnect", or just the simplicity of keeping the same default connection.
Two somewhat related fixes in this PR:
ConnectionLostEvent
s.