fix(lib): keep a replaced peer connection open for the reply in flight
Two connections to the same peer race whenever both sides dial at once: we open one to send a browse, the peer opens one to deliver a search reply. The registry keeps one actor per username, and until now stopped the older one on the spot, which closed the socket carrying our browse before the listing could arrive. Under load the stress gate lost up to two thirds of its browses that way. A replaced actor is now retired instead: it sends nothing new, keeps reading for thirty seconds so a reply already on the wire still reaches the client, and then stops itself. The peer closing a retired connection is expected and reported as a clean close, so it cannot fail transfers running on the connection that took over.
This commit is contained in:
@@ -126,14 +126,14 @@ impl PeerRegistry {
|
||||
actor.set_self_handle(handle);
|
||||
})
|
||||
.map_err(|e| format!("failed to spawn peer actor thread: {e}"))?;
|
||||
// Stop any actor already registered under this username so it does not
|
||||
// become an orphan pinning a pool worker forever. Eviction on the
|
||||
// replaced actor's later shutdown is identity-aware (keyed on its id),
|
||||
// so stopping it here cannot evict this new connection.
|
||||
// Retire any actor already registered under this username rather than
|
||||
// stop it: a reply to a request we sent on it may still be on the
|
||||
// wire. It stops itself once that grace is over, and the eviction its
|
||||
// shutdown triggers is keyed on its id, so it cannot evict this one.
|
||||
if let Some((_, old_handle)) =
|
||||
peers.insert(username.clone(), (id, handle.clone()))
|
||||
{
|
||||
let _ = old_handle.stop();
|
||||
let _ = old_handle.send(PeerMessage::Retire);
|
||||
debug!(
|
||||
"[peer_registry] Replaced existing peer actor for {}",
|
||||
username
|
||||
|
||||
Reference in New Issue
Block a user