fix: identity-aware peer eviction so a replaced actor can't evict its successor
When a healthy peer actor was replaced under the same username (both sides connecting at once, or two overlapping downloads to the same not-yet-connected peer), its graceful shutdown still emitted PeerDisconnected, and the client evicted by username — removing and stopping the NEWER actor now occupying the slot, tearing down the live connection and stalling that peer's downloads. Give each peer actor a unique id (assigned by the registry) and echo it in PeerDisconnected/PeerConnectFailed. The client now evicts via remove_peer_if(username, id), which removes the entry only when the reporting actor still owns the slot, so a stale actor's shutdown is a no-op. The brokered timeout no longer removes an actor by username either, avoiding the same aliasing. Add a registry unit test for the identity guard.
This commit is contained in:
@@ -61,6 +61,9 @@ pub struct PeerActor {
|
||||
/// replaced actor is later stopped — which would otherwise alias and evict
|
||||
/// a newer actor registered under the same username.
|
||||
disconnect_reported: bool,
|
||||
/// Registry-assigned unique id, echoed in terminal notifications so the
|
||||
/// client evicts only this actor and never a newer namesake.
|
||||
id: u64,
|
||||
}
|
||||
|
||||
impl PeerActor {
|
||||
@@ -71,6 +74,7 @@ impl PeerActor {
|
||||
reader: Option<MessageReader>,
|
||||
client_channel: Sender<ClientOperation>,
|
||||
own_username: String,
|
||||
id: u64,
|
||||
) -> Self {
|
||||
let outbound = stream.is_none();
|
||||
let connection_state = if stream.is_some() {
|
||||
@@ -93,6 +97,7 @@ impl PeerActor {
|
||||
outbound,
|
||||
established: false,
|
||||
disconnect_reported: false,
|
||||
id,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -449,9 +454,13 @@ impl PeerActor {
|
||||
// client can fall back to server-brokered connect. Anything else is a
|
||||
// normal disconnect.
|
||||
let op = if self.outbound && !self.established {
|
||||
ClientOperation::PeerConnectFailed(username)
|
||||
ClientOperation::PeerConnectFailed(self.id, username)
|
||||
} else {
|
||||
ClientOperation::PeerDisconnected(username, Some(error.into()))
|
||||
ClientOperation::PeerDisconnected(
|
||||
self.id,
|
||||
username,
|
||||
Some(error.into()),
|
||||
)
|
||||
};
|
||||
|
||||
if let Err(e) = self.client_channel.send(op) {
|
||||
@@ -471,7 +480,7 @@ impl PeerActor {
|
||||
|
||||
if let Err(e) = self
|
||||
.client_channel
|
||||
.send(ClientOperation::PeerDisconnected(username, None))
|
||||
.send(ClientOperation::PeerDisconnected(self.id, username, None))
|
||||
{
|
||||
error!("Failed to send disconnect notification: {}", e);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user