use std::sync::Arc;
use std::time::Duration;
use flowscope::correlate::{Attribution, BandwidthByKey, ByteSemantics};
use rustc_hash::FxHashMap;
use crate::protocol::FlowKey;
pub(crate) const OWNER_BW_WINDOW: Duration = Duration::from_secs(10);
pub(crate) const OWNER_BW_BUCKET: Duration = Duration::from_secs(1);
pub type AttributionHook = Arc<dyn Fn(&FlowKey) -> Option<Attribution> + Send + Sync + 'static>;
pub(crate) struct OwnerBandwidthState {
hook: AttributionHook,
owners: FxHashMap<FlowKey, Option<Attribution>>,
pub(crate) bw: BandwidthByKey<Attribution>,
pub(crate) unknown: BandwidthByKey<()>,
}
impl OwnerBandwidthState {
pub(crate) fn new(hook: AttributionHook, window: Duration, bucket: Duration) -> Self {
Self {
hook,
owners: FxHashMap::default(),
bw: BandwidthByKey::new_unbounded(window, bucket),
unknown: BandwidthByKey::new_unbounded(window, bucket),
}
}
pub(crate) fn record(&mut self, key: &FlowKey, bytes: u64, now: flowscope::Timestamp) {
let owner = *self.owners.entry(*key).or_insert_with(|| (self.hook)(key));
match owner {
Some(attr) => self.bw.record_tx(attr, bytes, now),
None => self.unknown.record_tx((), bytes, now),
}
}
pub(crate) fn forget(&mut self, key: &FlowKey) {
self.owners.remove(key);
}
}
impl Default for OwnerBandwidthState {
fn default() -> Self {
Self::new(
Arc::new(|_: &FlowKey| None),
OWNER_BW_WINDOW,
OWNER_BW_BUCKET,
)
}
}
pub struct OwnerBandwidthReport<'a> {
pub(crate) state: &'a OwnerBandwidthState,
pub(crate) now: flowscope::Timestamp,
}
impl OwnerBandwidthReport<'_> {
pub fn top(&self, n: usize) -> Vec<(Attribution, f64)> {
self.state.bw.top_k(n, self.now)
}
pub fn rate(&self, owner: Attribution) -> f64 {
self.state.bw.total_bps(&owner, self.now)
}
pub fn unknown_rate(&self) -> f64 {
self.state.unknown.total_bps(&(), self.now)
}
pub fn unknown_share(&self) -> f64 {
let known: f64 = self
.state
.bw
.top_k(usize::MAX, self.now)
.iter()
.map(|(_, r)| r)
.sum();
let unknown = self.unknown_rate();
let total = known + unknown;
if total > 0.0 { unknown / total } else { 0.0 }
}
pub fn semantics(&self) -> ByteSemantics {
self.state.bw.semantics()
}
pub fn to_snapshot(&self, n: usize) -> OwnerBandwidthSnapshot {
OwnerBandwidthSnapshot {
owners: self.top(n).into_iter().map(|(a, bps)| (a.0, bps)).collect(),
unknown_bps: self.unknown_rate(),
unknown_share: self.unknown_share(),
semantics: self.semantics().as_str(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct OwnerBandwidthSnapshot {
pub owners: Vec<(u64, f64)>,
pub unknown_bps: f64,
pub unknown_share: f64,
pub semantics: &'static str,
}