commit 17a569c703a682021c66214dfde3dd51b0a2f361 Author: Mingwei Samuel Date: Sat Oct 12 00:48:15 2019 -0700 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6936990 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..ac6e272 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "riven" +version = "0.0.1" +authors = ["Mingwei Samuel "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..9de84d7 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,11 @@ +mod req; + +use req::rate_limit; + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} diff --git a/src/req/mod.rs b/src/req/mod.rs new file mode 100644 index 0000000..f07548e --- /dev/null +++ b/src/req/mod.rs @@ -0,0 +1,2 @@ +pub mod rate_limit; +pub mod token_bucket; \ No newline at end of file diff --git a/src/req/rate_limit.rs b/src/req/rate_limit.rs new file mode 100644 index 0000000..fe91cc2 --- /dev/null +++ b/src/req/rate_limit.rs @@ -0,0 +1,6 @@ +use std::time::Duration; + +pub trait RateLimit { + fn get_retry_after_delay(&self) -> Duration; +} + diff --git a/src/req/token_bucket.rs b/src/req/token_bucket.rs new file mode 100644 index 0000000..8967692 --- /dev/null +++ b/src/req/token_bucket.rs @@ -0,0 +1,93 @@ +use std::collections::VecDeque; +use std::time::{Duration, Instant}; + +pub trait TokenBucket { + /// Get the duration til the next available token, or 0 duration if a token is available. + /// # Returns + /// Duration or 0 duration. + fn get_delay(&mut self) -> Duration; + + /// Gets n tokens, regardless of whether they are available. + /// # Parameters + /// * `n` - Number of tokens to take. + /// # Returns + /// True if the tokens were obtained without violating limits, false otherwise. + fn get_tokens(&mut self, n: usize) -> bool; + + /// Get the duration of this bucket. + /// # Returns + /// Duration of the bucket. + fn get_bucket_duration(&self) -> Duration; + + /// Get the total limit of this bucket per timespan. + /// # Returns + /// Total limit per timespan. + fn get_total_limit(&self) -> usize; +} + +struct VectorTokenBucket { + /// Duration of this TokenBucket. + duration: Duration, + // Total tokens available from this TokenBucket. + total_limit: usize, + // Record of timestamps. + timestamps: VecDeque, +} + +impl VectorTokenBucket { + fn create(duration: Duration, total_limit: usize) -> Self { + VectorTokenBucket { + duration: duration, + total_limit: total_limit, + timestamps: VecDeque::new(), + } + } + + fn update_state(&mut self) { + let cutoff = Instant::now() - self.duration; + while self.timestamps.back().map_or(false, |ts| ts < &cutoff) { + self.timestamps.pop_back(); + } + } +} + +impl TokenBucket for VectorTokenBucket { + + fn get_delay(&mut self) -> Duration { + self.update_state(); + if self.timestamps.len() < self.total_limit { + Duration::new(0, 0) + } + else { + let ts = *self.timestamps.get(self.total_limit - 1).unwrap(); + Instant::now().saturating_duration_since(ts) + } + } + + fn get_tokens(&mut self, n: usize) -> bool { + self.update_state(); + let now = Instant::now(); + + self.timestamps.reserve(n); + for _ in 0..n { + self.timestamps.push_front(now); + } + self.timestamps.len() <= self.total_limit + } + + fn get_bucket_duration(&self) -> Duration { + self.duration + } + + fn get_total_limit(&self) -> usize { + self.total_limit + } +} + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +}