docs(irtt-stats): Add documentation comments to public-facing types and fields

Add docstrings to clarify the purpose of statistics collection types and describe the units and behaviors of metric fields.
This commit is contained in:
Nils Andreas Svee committed 2026-05-13 15:15:41 +07:00
1 parent 19e0e7aab9
commit a180388dc6
3 files changed
+114 -3

No files matched your search

+86 -3
View File
@@ -1,4 +1,7 @@
//! Statistics aggregation for `irtt-client` events. //! Statistics aggregation for `irtt-client` events.
//!
//! The crate consumes `irtt-client` events and produces cumulative or rolling
//! snapshots for reporting and integration code.
#![forbid(unsafe_code)] #![forbid(unsafe_code)]
@@ -20,13 +23,22 @@ use rolling::RollingEvents;
pub use time_stats::TimeStats; pub use time_stats::TimeStats;
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// Configuration for statistics collection.
pub struct StatsConfig { pub struct StatsConfig {
/// How timing samples are retained for median-capable metrics.
pub samples: SampleMode, pub samples: SampleMode,
/// Number of recent normalized events retained for count-based rolling snapshots.
pub rolling_count: Option<usize>, pub rolling_count: Option<usize>,
/// Time span of recent normalized events retained for time-based rolling snapshots.
pub rolling_time: Option<Duration>, pub rolling_time: Option<Duration>,
} }
impl StatsConfig { impl StatsConfig {
/// Returns the default configuration for finite runs.
///
/// Finite mode retains exact samples where needed for medians and keeps
/// unbounded adjacent-sequence IPDV tracking so late adjacent replies can
/// still complete IPDV pairs.
pub fn finite() -> Self { pub fn finite() -> Self {
Self { Self {
samples: SampleMode::Exact, samples: SampleMode::Exact,
@@ -35,10 +47,11 @@ impl StatsConfig {
} }
} }
/// Configuration for long-running use. /// Returns a configuration for long-running use.
/// ///
/// This disables exact sample retention and bounds sequence-adjacent IPDV /// Continuous mode uses running statistics, does not retain exact samples
/// tracking. Exact arbitrary-late IPDV completion is finite-mode behavior. /// for medians, and bounds adjacent-sequence IPDV tracking for long-running
/// sessions.
pub fn continuous() -> Self { pub fn continuous() -> Self {
Self { Self {
samples: SampleMode::RunningOnly, samples: SampleMode::RunningOnly,
@@ -55,18 +68,26 @@ impl Default for StatsConfig {
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// Controls whether exact timing samples are retained.
pub enum SampleMode { pub enum SampleMode {
/// Keep only running statistics; exact medians are not available.
RunningOnly, RunningOnly,
/// Retain exact samples for metrics that report exact medians.
Exact, Exact,
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
/// Stateful statistics collector for `irtt-client` events.
///
/// A collector maintains cumulative statistics and, when configured, rolling
/// windows. Rolling snapshots are recomputed from retained normalized events.
pub struct StatsCollector { pub struct StatsCollector {
cumulative: CoreStats, cumulative: CoreStats,
rolling: RollingEvents, rolling: RollingEvents,
} }
impl StatsCollector { impl StatsCollector {
/// Creates a collector with the supplied configuration.
pub fn new(config: StatsConfig) -> Self { pub fn new(config: StatsConfig) -> Self {
Self { Self {
cumulative: CoreStats::new(config.samples), cumulative: CoreStats::new(config.samples),
@@ -74,6 +95,11 @@ impl StatsCollector {
} }
} }
/// Processes one client event and returns the per-event statistics update.
///
/// Updates currently report whether the event contributed a unique reply
/// timing sample and any adjacent-sequence IPDV pairs completed by this
/// event.
pub fn process(&mut self, event: &ClientEvent) -> EventStatsUpdate { pub fn process(&mut self, event: &ClientEvent) -> EventStatsUpdate {
let Some(stats_event) = normalize_event(event) else { let Some(stats_event) = normalize_event(event) else {
return EventStatsUpdate::default(); return EventStatsUpdate::default();
@@ -84,92 +110,149 @@ impl StatsCollector {
update update
} }
/// Returns a snapshot of all events processed by this collector.
pub fn snapshot(&self) -> Snapshot { pub fn snapshot(&self) -> Snapshot {
self.cumulative.snapshot() self.cumulative.snapshot()
} }
/// Returns the configured count-based rolling snapshot, if enabled.
///
/// The snapshot is recomputed from the retained rolling events.
pub fn rolling_count(&self) -> Option<Snapshot> { pub fn rolling_count(&self) -> Option<Snapshot> {
self.rolling.count_snapshot() self.rolling.count_snapshot()
} }
/// Returns the configured time-based rolling snapshot, if enabled.
///
/// The snapshot is recomputed from the retained rolling events.
pub fn rolling_time(&self) -> Option<Snapshot> { pub fn rolling_time(&self) -> Option<Snapshot> {
self.rolling.time_snapshot() self.rolling.time_snapshot()
} }
} }
#[derive(Debug, Clone, PartialEq, Eq, Default)] #[derive(Debug, Clone, PartialEq, Eq, Default)]
/// Per-event statistics produced by [`StatsCollector::process`].
pub struct EventStatsUpdate { pub struct EventStatsUpdate {
/// Whether the processed event contributed a unique reply timing sample.
pub contributed_sample: bool, pub contributed_sample: bool,
/// Adjacent-sequence IPDV pairs completed by the processed event.
pub ipdv_pairs: Vec<IpdvPairUpdate>, pub ipdv_pairs: Vec<IpdvPairUpdate>,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// Adjacent-sequence IPDV pair completed by a processed event.
pub struct IpdvPairUpdate { pub struct IpdvPairUpdate {
/// Sequence number of the earlier reply in the adjacent pair.
pub previous_seq: u32, pub previous_seq: u32,
/// Sequence number of the later reply in the adjacent pair.
pub current_seq: u32, pub current_seq: u32,
/// Absolute round-trip IPDV between the adjacent replies.
pub rtt_ipdv: Duration, pub rtt_ipdv: Duration,
/// Absolute send-side IPDV when send one-way delay is available for both replies.
pub send_ipdv: Option<Duration>, pub send_ipdv: Option<Duration>,
/// Absolute receive-side IPDV when receive one-way delay is available for both replies.
pub receive_ipdv: Option<Duration>, pub receive_ipdv: Option<Duration>,
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
/// Point-in-time statistics summary.
pub struct Snapshot { pub struct Snapshot {
/// Event counters grouped by event class.
pub events: EventCounts, pub events: EventCounts,
/// Packet and byte counters.
pub packets: PacketCounts, pub packets: PacketCounts,
/// Packet loss, duplicate, and late-packet percentages.
pub loss: LossStats, pub loss: LossStats,
/// Duration of send calls, in nanoseconds.
pub send_call: TimeStats, pub send_call: TimeStats,
/// Sender scheduling error, in nanoseconds.
pub timer_error: TimeStats, pub timer_error: TimeStats,
/// Round-trip timing statistics.
pub rtt: RttStats, pub rtt: RttStats,
/// Inter-packet delay variation statistics.
pub ipdv: IpdvStats, pub ipdv: IpdvStats,
/// One-way delay statistics.
pub one_way_delay: OneWayDelayStats, pub one_way_delay: OneWayDelayStats,
/// Server processing time statistics.
pub server_processing: ServerProcessingStats, pub server_processing: ServerProcessingStats,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
/// Counts of normalized client events.
pub struct EventCounts { pub struct EventCounts {
/// Probe send events processed.
pub sent_events: u64, pub sent_events: u64,
/// On-time unique echo replies processed.
pub echo_replies: u64, pub echo_replies: u64,
/// Late unique echo replies processed.
pub late_unique_replies: u64, pub late_unique_replies: u64,
/// Duplicate echo replies processed.
pub duplicate_replies: u64, pub duplicate_replies: u64,
/// Loss events processed.
pub loss_events: u64, pub loss_events: u64,
/// Warning events processed.
pub warning_events: u64, pub warning_events: u64,
/// Late replies that could not be matched to retained in-flight state.
pub untracked_late_replies: u64, pub untracked_late_replies: u64,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
/// Packet, byte, and server-reported receive counters.
pub struct PacketCounts { pub struct PacketCounts {
/// Probe packets sent by the local client.
pub packets_sent: u64, pub packets_sent: u64,
/// Reply packets received by the local client, including duplicates and late packets.
pub packets_received: u64, pub packets_received: u64,
/// Unique replies received by the local client.
pub unique_replies: u64, pub unique_replies: u64,
/// Duplicate replies received by the local client.
pub duplicates: u64, pub duplicates: u64,
/// Late reply packets received by the local client.
pub late_packets: u64, pub late_packets: u64,
/// Probe bytes sent by the local client.
pub bytes_sent: u64, pub bytes_sent: u64,
/// Reply bytes received by the local client.
pub bytes_received: u64, pub bytes_received: u64,
/// Server-reported packets received, when available.
pub server_packets_received: Option<u64>, pub server_packets_received: Option<u64>,
/// Server-reported receive window, when available.
pub server_received_window: Option<u64>, pub server_received_window: Option<u64>,
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
/// Round-trip time statistics.
pub struct RttStats { pub struct RttStats {
/// Effective RTT used for primary RTT reporting and IPDV input.
pub primary: TimeStats, pub primary: TimeStats,
/// Client-observed raw RTT.
pub raw: TimeStats, pub raw: TimeStats,
/// RTT adjusted for server processing time when available.
pub adjusted: TimeStats, pub adjusted: TimeStats,
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
/// Inter-packet delay variation statistics.
pub struct IpdvStats { pub struct IpdvStats {
/// Round-trip IPDV.
pub round_trip: TimeStats, pub round_trip: TimeStats,
/// Send-side IPDV, when send one-way delay is available.
pub send: TimeStats, pub send: TimeStats,
/// Receive-side IPDV, when receive one-way delay is available.
pub receive: TimeStats, pub receive: TimeStats,
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
/// One-way delay statistics.
pub struct OneWayDelayStats { pub struct OneWayDelayStats {
/// Client-to-server delay.
pub send_delay: TimeStats, pub send_delay: TimeStats,
/// Server-to-client delay.
pub receive_delay: TimeStats, pub receive_delay: TimeStats,
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
/// Server processing time statistics.
pub struct ServerProcessingStats { pub struct ServerProcessingStats {
/// Time spent processing a probe at the server.
pub processing: TimeStats, pub processing: TimeStats,
} }
+14
View File
@@ -1,15 +1,29 @@
use crate::PacketCounts; use crate::PacketCounts;
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
/// Packet loss, duplicate, and late-packet statistics.
///
/// Percentage fields are percentages from `0.0` to `100.0` in normal cases.
/// Directional upstream and downstream loss can be negative when
/// server-reported counts exceed local expectations.
pub struct LossStats { pub struct LossStats {
/// Locally inferred lost packets.
pub lost_packets: u64, pub lost_packets: u64,
/// Loss not attributed to a direction.
pub unknown_loss_packets: u64, pub unknown_loss_packets: u64,
/// Server-assisted upstream loss estimate, when server counts are available.
pub upstream_loss_packets: Option<i128>, pub upstream_loss_packets: Option<i128>,
/// Server-assisted downstream loss estimate, when server counts are available.
pub downstream_loss_packets: Option<i128>, pub downstream_loss_packets: Option<i128>,
/// Total packet loss percentage.
pub packet_loss_percent: f64, pub packet_loss_percent: f64,
/// Server-assisted upstream loss percentage.
pub upstream_loss_percent: f64, pub upstream_loss_percent: f64,
/// Server-assisted downstream loss percentage.
pub downstream_loss_percent: f64, pub downstream_loss_percent: f64,
/// Duplicate reply percentage.
pub duplicate_percent: f64, pub duplicate_percent: f64,
/// Late reply packet percentage.
pub late_packets_percent: f64, pub late_packets_percent: f64,
} }
+14
View File
@@ -1,19 +1,33 @@
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
/// Summary statistics for timing values.
///
/// All fields use nanoseconds except `variance_ns2`, which is nanoseconds
/// squared. Values may represent signed timing quantities such as scheduling
/// error or one-way delay.
pub struct TimeStats { pub struct TimeStats {
/// Number of samples included in this summary.
pub count: u64, pub count: u64,
/// Sum of all samples, in nanoseconds.
pub total_ns: i128, pub total_ns: i128,
/// Smallest sample, in nanoseconds.
pub min_ns: Option<i128>, pub min_ns: Option<i128>,
/// Largest sample, in nanoseconds.
pub max_ns: Option<i128>, pub max_ns: Option<i128>,
/// Arithmetic mean, in nanoseconds.
pub mean_ns: f64, pub mean_ns: f64,
/// Median, in nanoseconds, when exact samples were retained.
pub median_ns: Option<f64>, pub median_ns: Option<f64>,
/// Sample variance, in nanoseconds squared.
pub variance_ns2: f64, pub variance_ns2: f64,
} }
impl TimeStats { impl TimeStats {
/// Returns the sample standard deviation, in nanoseconds.
pub fn stddev_ns(&self) -> f64 { pub fn stddev_ns(&self) -> f64 {
self.variance_ns2.sqrt() self.variance_ns2.sqrt()
} }
/// Returns whether this summary contains no samples.
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.count == 0 self.count == 0
} }