refactor(client): use SignedDuration for one-way delay to support negative values

Allow one-way delay samples to represent negative values caused by wall-clock skew between client and server. Update CLI output and statistics collection to correctly handle and report these signed durations.
This commit is contained in:
Nils Andreas Svee committed 2026-05-17 23:55:01 +07:00
1 parent 0b16ee802d
commit 3b5336a487
7 files changed
+193 -19

No files matched your search

+2 -2
View File
@@ -163,10 +163,10 @@ impl ReplySample {
rtt_adjusted_ns: rtt.adjusted.map(signed_duration_ns),
send_delay_ns: one_way
.and_then(|sample| sample.client_to_server)
.map(duration_ns_i128),
.map(signed_duration_ns),
receive_delay_ns: one_way
.and_then(|sample| sample.server_to_client)
.map(duration_ns_i128),
.map(signed_duration_ns),
server_processing_ns: server_timing
.and_then(|timing| timing.processing)
.map(duration_ns_i128),
+2 -2
View File
@@ -72,8 +72,8 @@ fn reply_with_rtt(seq: u32, raw_ms: u64, rtt: RttSample) -> ClientEvent {
processing: Some(Duration::from_millis(1)),
}),
one_way: Some(OneWayDelaySample {
client_to_server: Some(Duration::from_millis(1)),
server_to_client: Some(Duration::from_millis(2)),
client_to_server: Some(SignedDuration::from_nanos(1_000_000)),
server_to_client: Some(SignedDuration::from_nanos(2_000_000)),
}),
received_stats: Some(ReceivedStatsSample {
count: Some(seq + 1),
+45 -1
View File
@@ -3,7 +3,9 @@ mod common;
use std::time::{Duration, Instant, UNIX_EPOCH};
use common::{adjusted_reply, sent, ts, unadjusted_late_reply, unadjusted_reply};
use irtt_client::{ClientEvent, ClientTimestamp, PacketMeta, RttSample, SignedDuration};
use irtt_client::{
ClientEvent, ClientTimestamp, OneWayDelaySample, PacketMeta, RttSample, SignedDuration,
};
use irtt_stats::{EventStatsUpdate, IpdvPairUpdate, SampleMode, StatsCollector, StatsConfig};
#[test]
@@ -280,6 +282,48 @@ fn server_processing_and_one_way_require_available_samples() {
assert_eq!(snapshot.packets.bytes_received, 128);
}
#[test]
fn negative_one_way_delay_samples_are_included_in_stats() {
let mut collector = StatsCollector::new(StatsConfig::finite());
let sent_at = ts(0);
let received_at = ClientTimestamp {
mono: sent_at.mono + Duration::from_millis(10),
wall: sent_at.wall + Duration::from_millis(10),
};
collector.process(&ClientEvent::EchoReply {
seq: 0,
remote: "127.0.0.1:2112".parse().unwrap(),
sent_at,
received_at,
rtt: RttSample {
raw: Duration::from_millis(10),
adjusted: None,
effective: SignedDuration::from_nanos(10_000_000),
},
server_timing: None,
one_way: Some(OneWayDelaySample {
client_to_server: Some(SignedDuration::from_nanos(-1_000_000)),
server_to_client: Some(SignedDuration::from_nanos(-2_000_000)),
}),
received_stats: None,
bytes: 64,
packet_meta: PacketMeta::default(),
});
let snapshot = collector.snapshot();
assert_eq!(snapshot.one_way_delay.send_delay.count, 1);
assert_eq!(snapshot.one_way_delay.send_delay.min_ns, Some(-1_000_000));
assert_eq!(snapshot.one_way_delay.send_delay.total_ns, -1_000_000);
assert_eq!(snapshot.one_way_delay.receive_delay.count, 1);
assert_eq!(
snapshot.one_way_delay.receive_delay.min_ns,
Some(-2_000_000)
);
assert_eq!(snapshot.one_way_delay.receive_delay.total_ns, -2_000_000);
}
#[test]
fn older_cumulative_server_receive_count_does_not_regress() {
let mut collector = StatsCollector::new(StatsConfig::finite());