Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions noq-proto/src/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3388,8 +3388,9 @@ impl Connection {
);
}
// Before removing the path, we fetch the final path stats via `Self::path_stats`.
// This updates some values for the last time.
let path_stats = self.path_stats.discard(&path_id);
// This ensures snapshot values (like rtt) are properly updated.
let path_stats = self.path_stats(path_id).unwrap_or_default();
self.path_stats.discard(&path_id);
Comment on lines +3391 to +3393

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oof this took me a while to realize that self.path_stats is doing a lot more than just return path stats, and self.path_stats.discard can't just do this work for me.

Thanks, nice catch!

self.partial_stats += path_stats;
self.paths.remove(&path_id);
self.spaces[SpaceId::Data].number_spaces.remove(&path_id);
Expand Down
4 changes: 2 additions & 2 deletions noq-proto/src/connection/stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ impl PathStatsMap {
/// Removes the stats for a given path.
///
/// Only do this once you are discarding the path.
pub(super) fn discard(&mut self, path_id: &PathId) -> PathStats {
self.0.remove(path_id).unwrap_or_default()
pub(super) fn discard(&mut self, path_id: &PathId) {
self.0.remove(path_id);
}
}
45 changes: 45 additions & 0 deletions noq-proto/src/tests/multipath.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2228,3 +2228,48 @@ fn regression_delayed_path_cids_blocked() -> TestResult {
}
Ok(())
}

#[test]
fn regression_discarded_path_stats_are_up_to_date() -> TestResult {
let _guard = subscribe();
let mut pair = ConnPair::builder().enable_multipath().connect();

let server_addr = pair.routes.public_server_addr();
let path_id = pair.open_path(
Client,
FourTuple::from_remote(server_addr),
PathStatus::Available,
)?;
pair.drive();

// Drain establishment events.
while pair.poll(Client).is_some() {}
while pair.poll(Server).is_some() {}

// Close the path and drive until both sides discard it.
pair.close_path(Client, path_id, 0u8.into())?;
pair.drive();

assert_matches!(
pair.poll(Client),
Some(Event::Path(PathEvent::Abandoned { id, .. })) if id == path_id
);
assert_matches!(
pair.poll(Server),
Some(Event::Path(PathEvent::Abandoned { id, .. })) if id == path_id
);

pair.drive();

let discarded_stats = assert_matches!(
pair.poll(Client),
Some(Event::Path(PathEvent::Discarded { id, path_stats })) if id == path_id
=> *path_stats
);

// After a full handshake + MTU probing on the second path, these must be non-zero.
assert_ne!(discarded_stats.cwnd, 0);
assert_ne!(discarded_stats.current_mtu, 0);

Ok(())
}
Loading