refactor(client): use SignedDuration for effective RTT to support negative values

Simplify RttSample by merging signed and unsigned fields into a single SignedDuration for adjusted and effective RTT. This ensures accurate reporting and statistics when server processing time exceeds raw RTT.
This commit is contained in:
Nils Andreas Svee committed 2026-05-17 21:30:51 +07:00
1 parent 37de7c96a1
commit bec56d4114
14 files changed
+207 -86

No files matched your search

+2 -9
View File
@@ -727,22 +727,15 @@ fn compute_rtt(
let server_processing = compute_server_processing(ts);
let adjusted = server_processing.and_then(|sp| raw.checked_sub(sp));
let effective = adjusted.unwrap_or(raw);
let adjusted_signed = server_processing.map(|sp| SignedDuration {
let adjusted = server_processing.map(|sp| SignedDuration {
ns: duration_ns_i128(raw) - duration_ns_i128(sp),
});
let effective_signed = adjusted_signed.unwrap_or(SignedDuration {
ns: duration_ns_i128(raw),
});
let effective = adjusted.unwrap_or_else(|| SignedDuration::from_duration(raw));
RttSample {
raw,
adjusted,
effective,
adjusted_signed,
effective_signed,
}
}
@@ -41,7 +41,11 @@ fn recv_once_decodes_echo_reply_and_emits_event() {
assert_eq!(*seq, 0);
assert_eq!(*bytes, echo_packet_len(false, &params));
assert!(rtt.raw > Duration::ZERO);
assert_eq!(rtt.effective, rtt.adjusted.unwrap_or(rtt.raw));
assert_eq!(
rtt.effective,
rtt.adjusted
.unwrap_or_else(|| SignedDuration::from_duration(rtt.raw))
);
assert!(received_stats.is_some());
let stats = received_stats.as_ref().unwrap();
assert_eq!(stats.count, Some(42));
@@ -18,13 +18,27 @@ fn server_processing_greater_than_raw_does_not_underflow() {
..Default::default()
},
);
assert!(rtt.adjusted.is_none());
assert_eq!(rtt.effective, rtt.raw);
assert_eq!(
rtt.adjusted_signed,
Some(SignedDuration { ns: -999_999_999 })
assert_eq!(rtt.adjusted, Some(SignedDuration { ns: -999_999_999 }));
assert_eq!(rtt.effective, SignedDuration { ns: -999_999_999 });
}
#[test]
fn effective_rtt_uses_raw_when_adjusted_rtt_is_unavailable() {
let base = Instant::now();
let rtt = compute_rtt(
&ClientTimestamp {
mono: base,
wall: SystemTime::now(),
},
&ClientTimestamp {
mono: base + Duration::from_nanos(42),
wall: SystemTime::now(),
},
&TimestampFields::default(),
);
assert_eq!(rtt.effective_signed, SignedDuration { ns: -999_999_999 });
assert_eq!(rtt.adjusted, None);
assert_eq!(rtt.effective, SignedDuration::from_duration(rtt.raw));
}
#[test]
+22 -4
View File
@@ -106,11 +106,15 @@ pub enum WarningKind {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RttSample {
/// Client-observed RTT from send to receive.
pub raw: Duration,
pub adjusted: Option<Duration>,
pub effective: Duration,
pub adjusted_signed: Option<SignedDuration>,
pub effective_signed: SignedDuration,
/// Signed RTT adjusted for server processing when server processing is available.
pub adjusted: Option<SignedDuration>,
/// Signed effective RTT used by stats and CLI reporting.
///
/// This is `adjusted` when server processing is available, otherwise `raw`
/// converted to a signed duration.
pub effective: SignedDuration,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -118,6 +122,20 @@ pub struct SignedDuration {
pub ns: i128,
}
impl SignedDuration {
pub fn from_duration(duration: Duration) -> Self {
Self {
ns: i128::try_from(duration.as_nanos()).unwrap_or(i128::MAX),
}
}
}
impl From<Duration> for SignedDuration {
fn from(value: Duration) -> Self {
Self::from_duration(value)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ServerTiming {
pub receive_wall_ns: Option<i64>,
+6 -3
View File
@@ -7,7 +7,7 @@ use std::time::Duration;
use irtt_client::{
Client, ClientConfig, ClientError, ClientEvent, OneWayDelaySample, ReceivedStatsSample,
RttSample, ServerTiming,
RttSample, ServerTiming, SignedDuration,
};
use irtt_proto::{Clock, Params, ReceivedStats, StampAt, TimestampFields};
@@ -69,7 +69,10 @@ fn timestamp_modes_drive_reply_event_timing_fields() {
assert!(reply.server_timing.is_none());
assert!(reply.one_way.is_none());
assert!(reply.rtt.adjusted.is_none());
assert_eq!(reply.rtt.effective, reply.rtt.raw);
assert_eq!(
reply.rtt.effective,
SignedDuration::from_duration(reply.rtt.raw)
);
assert!(reply.rtt.raw > Duration::ZERO);
}
StampAt::Send => {
@@ -349,7 +352,7 @@ fn assert_processing_subtracted(reply: ReplyView<'_>) {
assert_eq!(processing, Duration::from_nanos(1));
let adjusted = reply.rtt.adjusted.unwrap();
assert_eq!(reply.rtt.effective, adjusted);
assert!(adjusted < reply.rtt.raw);
assert!(adjusted.ns < SignedDuration::from_duration(reply.rtt.raw).ns);
}
fn assert_one_way_presence(