Files
rutster/crates/rutster-tap/src/metrics.rs
Aaron D. Lee 276dc5ac45
Some checks failed
CI / clippy (push) Has been cancelled
CI / test (1.85) (push) Has been cancelled
CI / test (stable) (push) Has been cancelled
CI / deny (push) Has been cancelled
CI / fmt (push) Has started running
slice-4 (dev-b): TapAudioPipe::barge_in_flush + advisory_tx + MockRealtimeBrain schedule (#9)
Co-authored-by: Aaron D. Lee <himself@adlee.work>
Co-committed-by: Aaron D. Lee <himself@adlee.work>
2026-07-04 01:43:41 +00:00

70 lines
2.8 KiB
Rust

//! # TapMetrics — the "observe" half of "drop + observe" (slice-1 §3.8)
//!
//! Atomic counters shared between `TapAudioPipe` (hot path — inbound/outbound
//! drops, playout overflow/underflow) and `TapClient` (seq gaps, unknown
//! frames, malformed frames, reconnect attempts). All ops are
//! `fetch_add(_, Ordering::Relaxed)` — we're using the counts for
//! observability, not synchronization, so relaxed ordering is correct +
//! cheapest. A snapshot read is taken at log time (e.g. once per second or
//! on tap-disconnect).
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
/// Bounded playout ring capacity (spec §4.1: 5 frames = 100 ms at 20 ms/frame).
/// Tunable *constant* (no runtime config in slice-2; a future-rung concern).
pub const TAP_PLAYOUT_FRAMES: usize = 5;
#[derive(Debug, Default)]
pub struct TapMetrics {
pub inbound_dropped: AtomicU64,
pub outbound_dropped: AtomicU64,
pub playout_overflow: AtomicU64,
pub playout_underflow: AtomicU64,
pub seq_gaps: AtomicU64,
pub unknown_frames: AtomicU64,
pub malformed_frames: AtomicU64,
pub reconnect_attempts: AtomicU64,
/// slice-4 §3.3: count of in-flight `audio_out` frames dropped from
/// `rx_audio_out` during `barge_in_flush`. The drain makes the resume
/// condition race-free — the first `audio_out` observed post-barge is
/// provably post-barge.
pub barge_drained_inflight: AtomicU64,
}
impl TapMetrics {
pub fn new() -> Arc<Self> {
Arc::new(Self::default())
}
/// Read a consistent-ish snapshot. Not atomic across fields (each is
/// independently read), but for log/observability purposes that's fine —
/// we're reporting "approximately this many X happened," not synchronizing.
pub fn snapshot(&self) -> MetricsSnapshot {
MetricsSnapshot {
inbound_dropped: self.inbound_dropped.load(Ordering::Relaxed),
outbound_dropped: self.outbound_dropped.load(Ordering::Relaxed),
playout_overflow: self.playout_overflow.load(Ordering::Relaxed),
playout_underflow: self.playout_underflow.load(Ordering::Relaxed),
seq_gaps: self.seq_gaps.load(Ordering::Relaxed),
unknown_frames: self.unknown_frames.load(Ordering::Relaxed),
malformed_frames: self.malformed_frames.load(Ordering::Relaxed),
reconnect_attempts: self.reconnect_attempts.load(Ordering::Relaxed),
barge_drained_inflight: self.barge_drained_inflight.load(Ordering::Relaxed),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct MetricsSnapshot {
pub inbound_dropped: u64,
pub outbound_dropped: u64,
pub playout_overflow: u64,
pub playout_underflow: u64,
pub seq_gaps: u64,
pub unknown_frames: u64,
pub malformed_frames: u64,
pub reconnect_attempts: u64,
pub barge_drained_inflight: u64,
}