Add post-grace peer recovery coordinator

This commit is contained in:
2026-06-20 15:37:33 -04:00
parent 5564af02f9
commit 2c93c1c24f
7 changed files with 578 additions and 28 deletions
+21 -2
View File
@@ -5,7 +5,7 @@ use iroh_gossip::proto::TopicId;
use tokio::sync::mpsc;
use tokio::sync::mpsc::Receiver;
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use async_trait::async_trait;
use tokio_stream::StreamExt;
use serde::{Serialize, Deserialize};
@@ -198,6 +198,10 @@ pub struct IrohGossipState {
secret_key: SecretKey,
self_state: Arc<Mutex<Option<PeerState>>>,
peers: Arc<Mutex<HashMap<EndpointId, PeerState>>>,
/// Previously verified peers whose live roster entry was removed by a
/// transient disconnect. Retained only so a later authenticated `Leave`
/// still reaches core and cancels background recovery.
disconnected_peers: Arc<Mutex<HashSet<EndpointId>>>,
event_tx: mpsc::Sender<RoomEvent>,
event_rx: Mutex<Option<mpsc::Receiver<RoomEvent>>>,
active_topic: Mutex<Option<tokio::task::JoinHandle<()>>>,
@@ -223,6 +227,7 @@ impl IrohGossipState {
secret_key,
self_state: Arc::new(Mutex::new(None)),
peers: Arc::new(Mutex::new(HashMap::new())),
disconnected_peers: Arc::new(Mutex::new(HashSet::new())),
event_tx,
event_rx: Mutex::new(Some(event_rx)),
active_topic: Mutex::new(None),
@@ -295,6 +300,7 @@ impl RoomState for IrohGossipState {
let event_tx = self.event_tx.clone();
let peers = self.peers.clone();
let disconnected_peers = self.disconnected_peers.clone();
let address_lookup = self.address_lookup.clone();
let self_state_clone = self.self_state.clone();
let gossip_sender_clone = gossip_sender.clone();
@@ -393,6 +399,7 @@ impl RoomState for IrohGossipState {
// peer-supplied: cap/validate once at ingest
// so invalid offers never render a Watch button.
state.sharing = state.sharing.and_then(crate::screenshare::sanitize_ticket);
disconnected_peers.lock().unwrap().remove(&payload.author);
let (is_new, state_changed) = {
let mut peer_map = peers.lock().unwrap();
let is_new = !peer_map.contains_key(&payload.author);
@@ -423,7 +430,11 @@ impl RoomState for IrohGossipState {
GossipMessage::Leave => {
crate::log_msg(&format!("Gossip peer leave request from author={:?}", payload.author));
let removed = peers.lock().unwrap().remove(&payload.author).is_some();
if removed {
let was_disconnected = disconnected_peers
.lock()
.unwrap()
.remove(&payload.author);
if removed || was_disconnected {
let _ = event_tx.send(RoomEvent::PeerLeft(payload.author)).await;
}
}
@@ -472,6 +483,7 @@ impl RoomState for IrohGossipState {
// cached presence entry; a rejoin re-announces as new.
let removed = peers.lock().unwrap().remove(&peer_id).is_some();
if removed {
disconnected_peers.lock().unwrap().insert(peer_id);
crate::log_msg(&format!("Peer connection lost (NeighborDown): {:?}", peer_id));
let _ = event_tx.send(RoomEvent::PeerConnectionLost(peer_id)).await;
}
@@ -547,6 +559,12 @@ impl RoomState for IrohGossipState {
.map_err(|e| NetError::Gossip(e.to_string()))
}
fn mark_peer_disconnected(&self, peer_id: EndpointId) {
if self.peers.lock().unwrap().remove(&peer_id).is_some() {
self.disconnected_peers.lock().unwrap().insert(peer_id);
}
}
async fn send_chat(&self, text: String) -> Result<(), NetError> {
let name = {
let guard = self.self_state.lock().unwrap();
@@ -602,6 +620,7 @@ impl RoomState for IrohGossipState {
}
self.peers.lock().unwrap().clear();
self.disconnected_peers.lock().unwrap().clear();
Ok(())
}