use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
#[derive(Debug, Default)]
pub struct TransportStats {
inbound: AtomicU64,
inbound_dropped: AtomicU64,
outbound: AtomicU64,
outbound_dropped: AtomicU64,
presence_dropped: AtomicU64,
refused: AtomicU64,
}
impl TransportStats {
pub fn new() -> Arc<Self> {
Arc::new(Self::default())
}
pub fn inbound(&self) -> u64 {
self.inbound.load(Ordering::Relaxed)
}
pub fn inbound_dropped(&self) -> u64 {
self.inbound_dropped.load(Ordering::Relaxed)
}
pub fn outbound(&self) -> u64 {
self.outbound.load(Ordering::Relaxed)
}
pub fn outbound_dropped(&self) -> u64 {
self.outbound_dropped.load(Ordering::Relaxed)
}
pub fn presence_dropped(&self) -> u64 {
self.presence_dropped.load(Ordering::Relaxed)
}
pub fn refused(&self) -> u64 {
self.refused.load(Ordering::Relaxed)
}
pub fn record_refused(&self) {
self.refused.fetch_add(1, Ordering::Relaxed);
}
pub(crate) fn record_inbound(&self, dropped: bool) {
self.inbound.fetch_add(1, Ordering::Relaxed);
if dropped {
self.inbound_dropped.fetch_add(1, Ordering::Relaxed);
}
}
pub(crate) fn record_outbound(&self, sent: u64, dropped: u64) {
self.outbound.fetch_add(sent, Ordering::Relaxed);
self.outbound_dropped.fetch_add(dropped, Ordering::Relaxed);
}
pub(crate) fn record_presence_dropped(&self) {
self.presence_dropped.fetch_add(1, Ordering::Relaxed);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_drop_count_needs_its_denominator() {
let stats = TransportStats::new();
assert_eq!((stats.inbound(), stats.inbound_dropped()), (0, 0), "an idle transport");
stats.record_inbound(false);
stats.record_inbound(true);
assert_eq!((stats.inbound(), stats.inbound_dropped()), (2, 1), "a transport losing half of what it carries");
}
#[test]
fn presence_drops_are_counted_apart_from_traffic() {
let stats = TransportStats::new();
stats.record_outbound(100, 40);
stats.record_presence_dropped();
assert_eq!(stats.outbound_dropped(), 40);
assert_eq!(stats.presence_dropped(), 1);
}
}