//! Per-connection rate limiting and metrics for inbound messages. use commonware_runtime::{ Clock, RateLimiter, telemetry::metrics::{CounterFamily, raw::Counter}, }; use std::hash::Hash; /// Rate limiter and counters for one inbound message type on one connection. pub(crate) struct Throttle { limiter: RateLimiter, received: Counter, limited: Counter, } impl Throttle { pub(crate) fn new( limiter: RateLimiter, received: &CounterFamily, limited: &CounterFamily, label: &L, ) -> Self { Self { limiter, received: received.get_or_create_owned(label), limited: limited.get_or_create_owned(label), } } /// Records a received message and, if `limit` is set, waits until the rate limiter /// admits it. pub(crate) async fn receive(&self, limit: bool) { self.received.inc(); if !limit || self.limiter.check().is_ok() { return; } self.limited.inc(); // A rejected check does not consume a cell, so wait until one is taken let clock = self.limiter.clock(); while let Err(not_until) = self.limiter.check() { clock.sleep_until(not_until.earliest_possible()).await; } } } #[cfg(test)] mod tests { use super::*; use commonware_runtime::{ Quota, Runner as _, Supervisor as _, deterministic, telemetry::metrics::MetricsExt as _, }; use commonware_utils::NZU32; use std::time::Duration; type Label = [(&'static str, &'static str); 1]; const LABEL: Label = [("message", "data_0")]; #[test] fn test_receive() { deterministic::Runner::default().start(|context| async move { let received: CounterFamily