Skip to content

Commit

Permalink
Prepare for First Publish (#1)
Browse files Browse the repository at this point in the history
Publish!
  • Loading branch information
BenD0G authored Apr 6, 2024
1 parent 40dd1b1 commit 9515268
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 24 deletions.
9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
[package]
name = "weighted_rate_limiter"
version = "0.1.0"
version = "0.1.1"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "A weighted rate limiter"
repository = "https://github.com/BenD0G/weighted_rate_limiter"
keywords = ["weighted", "rate", "limiter", "limit", "throttle"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
futures = "0.3"
queues = "1.1"
tokio = { version = "1.15", features = ["time"] }

[dev-dependencies]
futures = "0.3"
pin-project = "1.0"
rand = "0.8"
tokio = { version = "1.15", features = ["full", "macros", "test-util"] }

Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Weighted Rate Limiter
Some API's have usage limits where different endpoints or actions have different costs - this is often implemented as the caller having a certain amount of weight that they are allowed to use per a given time period. This crate allows you to implement a rate limiter that enforces these limits - more specifically by queueing up futures that will be executed when the weight becomes available.

Crate for rate-limiting, possibly with different weights for different calls (as is the case for weighted API calls).
See [docs.rs](https://docs.rs/weighted_rate_limiter/latest/weighted_rate_limiter/) for usage.

# What we want
1. To have a queue-like structure where we can reserve some weight, with that reserved weight being returned to the available pool after a specified duration.
# To Note
This crate is very new, and has not been thoroughly tested.
6 changes: 3 additions & 3 deletions examples/hello_world.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! Construct a limiter that can fire 5 times every 5 seconds.
//! Repeatedly try to reserve a random amount of weight.
//! Construct a limiter that can fire jobs with total weight 5 every 2 seconds.
//! Jobs are given a random amount of weight.
use weighted_rate_limiter::RateLimiter;

Expand Down Expand Up @@ -32,7 +32,7 @@ async fn main() {
let weight: u64 = rng.gen_range(1..4);
let fut = make_future(weight, i, &start);
let rate_limited_fut = rate_limiter.rate_limit_future(fut, weight);
futures.push(Box::pin(rate_limited_fut));
futures.push(rate_limited_fut);
}

join_all(futures).await;
Expand Down
31 changes: 31 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,34 @@
//! # Weighted Rate Limiter
//! Some API's have usage limits where different endpoints or actions have different costs - this is often implemented as the caller having a certain amount of weight that they are allowed to use per a given time period. This crate allows you to implement a rate limiter that enforces these limits - more specifically by queueing up futures that will be executed when the weight becomes available.
//!
//! # Example
//! Here, we create a rate limiter that allows a total weight of 2 every 1 second. We then create two futures: one that returns 1 and one that returns "Hello" (note the different return types). We then rate limit these futures with weights 1 and 2 respectively. The first future will be executed immediately, while the second will be executed after 1 second, when the weight allowance has refreshed.
//! ```
//! # use tokio::time::Instant;
//! # use weighted_rate_limiter::RateLimiter;
//! # use std::time::Duration;
//! # use futures::join;
//! # #[tokio::main]
//! # async fn main() {
//! let rate_limiter = RateLimiter::new(2, Duration::from_secs(1));
//! let start = Instant::now();
//! let fut1 = async { println!("Returning 1 at {:?}", Instant::now() - start); 1 };
//! let fut2 = async { println!("Returning 'Hello' at {:?}", Instant::now() - start); "Hello!" };
//! let rate_limited_fut1 = rate_limiter.rate_limit_future(fut1, 1);
//! let rate_limited_fut2 = rate_limiter.rate_limit_future(fut2, 2);
//! println!("{:?}", join!(rate_limited_fut1, rate_limited_fut2));
//!
//! // Returning 1 at 5.2µs
//! // Returning 'Hello' at 1.0004935s
//! // (1, "Hello!")
//! # }
//! ```
//!
//! # Limitations
//! This crate is very new, and has not been tested in production.
//!
//! Futures are executed in the order they are rate limited, not in the order they are created.
mod rate_limiter;
mod weight_manager;

Expand Down
16 changes: 2 additions & 14 deletions src/rate_limiter.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,3 @@
//! Provides a struct for rate-limiting Future executions.
//!
//! The basic flow is:
//! 1. Pass your Future into the machine
//! 2. This is added to a queue and waits to be processed
//! 3. A future is returned that, when polled:
//! a) If the underlying future is pending, returns pending ("I'm working on it, ok??")
//! b) If waiting,
//!
//!
//! Notes for next time: expose another bit of API on the Limiter that returns the amount of time
//! until the next blcok of weight will expire.
//! That way you can creat a sleep() here that depends on that and might blow this whole async malarkey wide open.
use crate::WeightManager;

use std::cell::RefCell;
Expand All @@ -28,6 +14,7 @@ struct QueueData {
job_id: u64,
}

/// A rate limiter that enforces a weight limit over a given time period.
pub struct RateLimiter {
limiter: WeightManager,
queued_jobs: RefCell<VecDeque<QueueData>>,
Expand All @@ -49,6 +36,7 @@ impl RateLimiter {
self.limiter.time_of_next_weight_released().map(sleep_until)
}

/// Returns a future that will be executed when the weight is available.
pub async fn rate_limit_future<T, F>(&self, future: F, weight: u64) -> T
where
F: Future<Output = T>,
Expand Down
2 changes: 1 addition & 1 deletion src/weight_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ impl WeightManager {
*self.remaining_weight.borrow_mut() += weight_reservation.reserved_weight;
queue.remove().unwrap();
} else {
break;
break; // The next item to be released is still in the future
}
}
}
Expand Down

0 comments on commit 9515268

Please sign in to comment.