fix(client): timestamp probes after successful send

Moves the client userspace probe sent_at measurement to immediately
after a successful UDP send, improving raw RTT and the userspace
upstream-OWD fallback by excluding the socket send-call interval and
the small amount of local work preceding it.

All fallible probe-commit preflight (timeout deadline checked_add,
capacity/collision checks) still runs before the send, from a private
pre-send anchor (SessionMachine::finalize_probe_commit). The public
sent_at is now supplied afterward to an infallible
SessionMachine::commit_probe_sent, so DurationOverflow still guarantees
nothing was transmitted and a WouldBlock try_send still commits
nothing.

The operational probe timeout deadline stays anchored to the pre-send
timestamp on purpose, not to the new post-send sent_at, preserving
existing timeout/loss timing. Linux kernel TX_SOFTWARE plausibility
keeps its own pre-send wall-clock lower bound
(PendingProbe::tx_not_before_wall) instead of the post-send sent_at,
since a legitimate TX_SOFTWARE timestamp can be generated while the
send syscall is still in flight and would otherwise be wrongly
rejected.

Also flags (without touching production code) that irtt-stats'
ReplySample::from_reply_parts feeds the public sent_at directly into
IpdvSample.client_send_mono/client_send_wall_ns, so the upstream/send
IPDV component naturally shifts along with the sent_at semantics
change; per scope this is reported rather than compensated for.
This commit is contained in:
Nils Andreas Svee committed 2026-08-15 19:26:57 +07:00
1 parent 0ca36f2192
commit a3896376da
10 files changed
+502 -128

No files matched your search

+8 -1
View File
@@ -689,12 +689,14 @@ fn probe_false_readiness_resamples_all_timestamps() {
client.test_hooks.probe_timestamps.borrow_mut().extend([
ProbeSendTimestamps {
permission_at: scheduled_at + Duration::from_millis(1),
send_anchor: first_sent,
sent_at: first_sent,
send_call_start: scheduled_at + Duration::from_millis(4),
send_finished_at: scheduled_at + Duration::from_millis(6),
},
ProbeSendTimestamps {
permission_at: scheduled_at + Duration::from_millis(8),
send_anchor: second_sent,
sent_at: second_sent,
send_call_start: scheduled_at + Duration::from_millis(12),
send_finished_at: scheduled_at + Duration::from_millis(17),
@@ -741,6 +743,7 @@ fn public_timeout_polling_remains_exhaustive() {
wall: SystemTime::UNIX_EPOCH,
},
timeout_at: now - Duration::from_secs(1),
tx_not_before_wall: SystemTime::UNIX_EPOCH,
kernel_tx_timestamp: None,
});
}
@@ -803,7 +806,7 @@ fn stale_retained_probe_is_rejected_before_send() {
.unwrap();
client
.machine
.commit_probe_sent(commit, advanced.bytes.len());
.commit_probe_sent(commit, ClientTimestamp::now(), advanced.bytes.len());
let attempts = client.test_hooks.send_attempts.get();
assert!(matches!(
@@ -832,6 +835,10 @@ fn finite_duration_boundary_clears_preparation_without_send() {
.borrow_mut()
.push_back(ProbeSendTimestamps {
permission_at: start + Duration::from_millis(20),
send_anchor: ClientTimestamp {
wall: SystemTime::UNIX_EPOCH,
mono: start + Duration::from_millis(21),
},
sent_at: ClientTimestamp {
wall: SystemTime::UNIX_EPOCH,
mono: start + Duration::from_millis(21),