From 28ac542bc9f58936c445b45467359604609e001c Mon Sep 17 00:00:00 2001 From: Bryan Stitt Date: Sat, 25 Feb 2023 14:40:22 -0800 Subject: [PATCH] add simple rate-counter --- Cargo.lock | 8 ++++++++ Cargo.toml | 1 + rate-counter/Cargo.toml | 9 +++++++++ rate-counter/src/lib.rs | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 53 insertions(+) create mode 100644 rate-counter/Cargo.toml create mode 100644 rate-counter/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index d32c4082..649a1221 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3726,6 +3726,14 @@ dependencies = [ "rand_core", ] +[[package]] +name = "rate-counter" +version = "0.1.0" +dependencies = [ + "flume", + "tokio", +] + [[package]] name = "rayon" version = "1.6.1" diff --git a/Cargo.toml b/Cargo.toml index 2c2e0f74..9c7db66d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "deferred-rate-limiter", "entities", "migration", + "rate-counter", "redis-rate-limiter", "thread-fast-rng", "web3_proxy", diff --git a/rate-counter/Cargo.toml b/rate-counter/Cargo.toml new file mode 100644 index 00000000..7185ecd2 --- /dev/null +++ b/rate-counter/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "rate-counter" +version = "0.1.0" +authors = ["Bryan Stitt "] +edition = "2021" + +[dependencies] +flume = "0.10.14" +tokio = { version = "1.25.0", features = ["time"] } diff --git a/rate-counter/src/lib.rs b/rate-counter/src/lib.rs new file mode 100644 index 00000000..11a8359e --- /dev/null +++ b/rate-counter/src/lib.rs @@ -0,0 +1,35 @@ +//! A counter of events in a time period. +use std::collections::VecDeque; +use tokio::time::{Duration, Instant}; + +/// Measures ticks in a time period. +#[derive(Debug)] +pub struct RateCounter { + period: Duration, + items: VecDeque, +} + +impl RateCounter { + pub fn new(period: Duration) -> Self { + let items = VecDeque::new(); + + Self { period, items } + } + + /// update the counter and return the rate for the current period + /// true if the current time should be counted + pub fn update(&mut self, tick: bool) -> usize { + let now = Instant::now(); + let too_old = now - self.period; + + while self.items.front().map_or(false, |t| *t < too_old) { + self.items.pop_front(); + } + + if tick { + self.items.push_back(now); + } + + self.items.len() + } +}