fetcher: Guard fetched against stale node results

- Clear active[rid] only when the result's node matches the node that
  started the fetch; mismatched results now report NotFound
- Prevent a late completion from a disconnected peer clearing a newer
  fetch for the same repo started by a different node
- Add test covering the stale-from mismatch path
This commit is contained in:
Daniel Norman 2026-06-19 23:26:39 +02:00 committed by Fintan Halpenny
parent 7b910476c3
commit 4a05e52ba1
1 changed files with 16 additions and 4 deletions

View File

@ -203,16 +203,28 @@ impl FetcherState {
}
}
/// Process a [`Fetched`] command, which removes the given fetch from the set of active fetches.
/// Process a [`Fetched`] command, which removes the given fetch from the
/// set of active fetches, but only if the active fetch was from the given
/// node.
///
/// Note that this is agnostic of whether the fetch succeeded or failed.
///
/// The caller will be notified if the completed fetch did not exist in the active set.
///
/// [`Fetched`]: command::Fetched
pub fn fetched(&mut self, command::Fetched { from, rid }: command::Fetched) -> event::Fetched {
match self.active.remove(&rid) {
None => event::Fetched::NotFound { from, rid },
// Only the node that started the active fetch may complete it. A result from any
// other node is stale — e.g. a late completion delivered after the peer disconnected
// and the repo was re-fetched from elsewhere. Clearing the entry on a mismatch would
// drop the wrong (newer) fetch, so we leave it and report `NotFound`.
match self.active.get(&rid) {
Some(ActiveFetch {
from: active_from, ..
}) if *active_from == from => match self.active.remove(&rid) {
Some(ActiveFetch { from, refs }) => event::Fetched::Completed { from, rid, refs },
None => event::Fetched::NotFound { from, rid },
},
_ => event::Fetched::NotFound { from, rid },
}
}