From 7b910476c367f70bca91ad8e72195ad712380531 Mon Sep 17 00:00:00 2001 From: Daniel Norman Date: Fri, 19 Jun 2026 22:06:25 +0200 Subject: [PATCH] node/wire: Always report fetch results to the service A fetch result was discarded whenever the peer was no longer `Connected` by the time the worker reported, and queued fetches were dropped silently when the peer disconnected before the `Io::Fetch` was processed. In both cases the fetcher's `active` entry for the repo was never cleared. Since that entry is keyed by repo, it blocked the repository from being fetched from any node until the process restarted. - Report the result in `worker_result` even when the peer is no longer connected, instead of returning early - Report a failed fetch when an `Io::Fetch` is dropped for a disconnected peer, so the active entry is cleared - Flip the wire regression test to assert the entry is now cleared on disconnect --- crates/radicle-node/src/wire.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/crates/radicle-node/src/wire.rs b/crates/radicle-node/src/wire.rs index bebca675..72319eaa 100644 --- a/crates/radicle-node/src/wire.rs +++ b/crates/radicle-node/src/wire.rs @@ -414,10 +414,10 @@ where .push_back(Action::Send(fd, frame.encode_to_vec())); } } else { - // If the peer disconnected, we'll get here, but we still want to let the service know - // about the fetch result, so we don't return here. - log::debug!(target: "wire", "Peer {nid} is not connected; ignoring fetch result"); - return; + // If the peer disconnected, we still let the service know about the fetch result. + // Otherwise the fetcher's `active` entry for this repo is never cleared, which blocks + // the repository from being fetched from any node until the node restarts. + log::debug!(target: "wire", "Peer {nid} is not connected; reporting fetch result anyway"); }; // Only call into the service if we initiated this fetch. @@ -1025,9 +1025,18 @@ where else { // Nb. It's possible that a peer is disconnected while an `Io::Fetch` // is in the service's i/o buffer. Since the service may not purge the - // buffer on disconnect, we should just ignore i/o actions that don't - // have a connected peer. + // buffer on disconnect, we drop fetches that don't have a connected peer. + // We must still report the failure so the fetcher clears its `active` + // entry; otherwise the repository can no longer be fetched from any node. log::debug!(target: "wire", "Peer {remote} is not connected: dropping fetch"); + self.service.fetched( + rid, + remote, + Err(crate::worker::FetchError::Io(io::Error::new( + io::ErrorKind::NotConnected, + "peer disconnected before fetch could start", + ))), + ); continue; }; let (stream, channels) = streams.open(