feat(client): expose probe send commitment through typed receipts
This commit is contained in:
18 files changed
+322
-122
No files matched your search
@@ -0,0 +1,80 @@
|
||||
use std::{error::Error, fmt, net::SocketAddr, time::Duration};
|
||||
|
||||
use crate::{ClientError, ClientEvent, ClientTimestamp};
|
||||
|
||||
/// An Echo datagram accepted by the UDP socket and committed in session state.
|
||||
///
|
||||
/// The wire sequence and sent count have advanced, and the probe is pending.
|
||||
/// This receipt remains authoritative even if subsequent validation or kernel
|
||||
/// TX timestamp processing fails. It carries transport facts only; managed
|
||||
/// scheduling annotations belong on the corresponding [`ClientEvent::EchoSent`].
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct SendReceipt {
|
||||
/// Committed wire sequence number.
|
||||
pub seq: u32,
|
||||
/// Resolved remote socket address.
|
||||
pub remote: SocketAddr,
|
||||
/// Public measurement timestamp sampled immediately after socket acceptance.
|
||||
/// The private pre-send anchor still determines the timeout deadline.
|
||||
pub sent_at: ClientTimestamp,
|
||||
/// Number of bytes the socket reported accepting.
|
||||
pub bytes: usize,
|
||||
/// Time spent in the successful socket send call.
|
||||
pub send_call: Duration,
|
||||
}
|
||||
|
||||
impl From<SendReceipt> for ClientEvent {
|
||||
/// Construct the committed send event without scheduling annotations.
|
||||
fn from(receipt: SendReceipt) -> Self {
|
||||
Self::EchoSent {
|
||||
seq: receipt.seq,
|
||||
remote: receipt.remote,
|
||||
scheduled_at: None,
|
||||
sent_at: receipt.sent_at,
|
||||
bytes: receipt.bytes,
|
||||
send_call: receipt.send_call,
|
||||
timer_error: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Probe-send failure with an explicit socket-acceptance commitment boundary.
|
||||
///
|
||||
/// There is deliberately no blanket conversion from [`ClientError`]: each send
|
||||
/// failure must be classified at the point where its commitment is known.
|
||||
#[derive(Debug)]
|
||||
pub enum SendProbeError {
|
||||
/// No datagram was accepted and no probe state or sequence was committed.
|
||||
NotCommitted(ClientError),
|
||||
/// The probe was committed, then post-send processing failed.
|
||||
/// The pending probe and advanced sequence/count remain committed.
|
||||
AfterCommit {
|
||||
/// Authoritative receipt for the accepted send.
|
||||
receipt: SendReceipt,
|
||||
/// Operational cause of the post-send failure.
|
||||
source: Box<ClientError>,
|
||||
},
|
||||
}
|
||||
|
||||
impl fmt::Display for SendProbeError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::NotCommitted(source) => write!(f, "probe send was not committed: {source}"),
|
||||
Self::AfterCommit { source, .. } => {
|
||||
write!(
|
||||
f,
|
||||
"probe send was committed, but post-send processing failed: {source}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for SendProbeError {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
Some(match self {
|
||||
Self::NotCommitted(source) => source,
|
||||
Self::AfterCommit { source, .. } => source.as_ref(),
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user