use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
#[derive(Debug, Default)]
pub struct TransportStats {
inbound: AtomicU64,
inbound_dropped: AtomicU64,
inbound_shed: AtomicU64,
outbound: AtomicU64,
outbound_bytes: AtomicU64,
outbound_dropped: AtomicU64,
outbound_withheld: AtomicU64,
presence_dropped: AtomicU64,
refused: AtomicU64,
pending: AtomicU64,
admitted: AtomicU64,
timed_out: AtomicU64,
over_cap: 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 inbound_shed(&self) -> u64 {
self.inbound_shed.load(Ordering::Relaxed)
}
pub fn outbound(&self) -> u64 {
self.outbound.load(Ordering::Relaxed)
}
pub fn outbound_bytes(&self) -> u64 {
self.outbound_bytes.load(Ordering::Relaxed)
}
pub fn outbound_dropped(&self) -> u64 {
self.outbound_dropped.load(Ordering::Relaxed)
}
pub fn outbound_withheld(&self) -> u64 {
self.outbound_withheld.load(Ordering::Relaxed)
}
pub(crate) fn record_outbound_withheld(&self) {
self.outbound_withheld.fetch_add(1, 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 fn pending(&self) -> u64 {
self.pending.load(Ordering::Relaxed)
}
pub fn admitted(&self) -> u64 {
self.admitted.load(Ordering::Relaxed)
}
pub fn timed_out(&self) -> u64 {
self.timed_out.load(Ordering::Relaxed)
}
pub fn over_cap(&self) -> u64 {
self.over_cap.load(Ordering::Relaxed)
}
pub(crate) fn pending_begin(&self) {
self.pending.fetch_add(1, Ordering::Relaxed);
}
pub(crate) fn pending_end(&self) {
self.pending.fetch_sub(1, Ordering::Relaxed);
}
pub(crate) fn record_admitted(&self) {
self.admitted.fetch_add(1, Ordering::Relaxed);
}
pub(crate) fn record_timed_out(&self) {
self.timed_out.fetch_add(1, Ordering::Relaxed);
}
pub(crate) fn record_over_cap(&self) {
self.over_cap.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_inbound_shed(&self) {
self.inbound_shed.fetch_add(1, Ordering::Relaxed);
}
pub(crate) fn record_outbound(&self, sent: u64, dropped: u64, bytes: u64) {
self.outbound.fetch_add(sent, Ordering::Relaxed);
self.outbound_bytes.fetch_add(bytes, 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, 6_400);
stats.record_presence_dropped();
assert_eq!(stats.outbound_dropped(), 40);
assert_eq!(stats.outbound_bytes(), 6_400);
assert_eq!(stats.presence_dropped(), 1);
}
}