Skip to content

Commit

Permalink
rate limiter can use reference for RateLimit (#10)
Browse files Browse the repository at this point in the history
The internal function accepts a ref, and the `RateLimiter` functions don't do anything but pipe the parameter. It's safe to only require a ref here to avoid the clone.
  • Loading branch information
lytefast authored Jul 2, 2024
1 parent 16dfc77 commit 0c878b9
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 49 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "gcra"
version = "0.5.0"
version = "0.6.0"
edition = "2021"
authors = ["Sam Shih <[email protected]>"]
license = "MIT"
Expand Down
6 changes: 3 additions & 3 deletions examples/rate_limiter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ async fn main() -> Result<(), GcraError> {
let rate_limit = RateLimit::per_sec(2);
let rl = RateLimiter::with_shards(CACHE_CAPACITY, WORKER_SHARD_COUNT);

rl.check("key", rate_limit.clone(), 1).await?;
rl.check("key", rate_limit.clone(), 1).await?;
rl.check("key", &rate_limit, 1).await?;
rl.check("key", &rate_limit, 1).await?;

match rl.check("key", rate_limit.clone(), 1).await {
match rl.check("key", &rate_limit, 1).await {
Err(GcraError::DeniedUntil { next_allowed_at }) => {
print!("Denied: Request next at {:?}", next_allowed_at);
Ok(())
Expand Down
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@
//! let rate_limit = RateLimit::per_sec(2);
//! let rl = RateLimiter::new(4);
//!
//! rl.check("key", rate_limit.clone(), 1).await?;
//! rl.check("key", rate_limit.clone(), 1).await?;
//! rl.check("key", &rate_limit, 1).await?;
//! rl.check("key", &rate_limit, 1).await?;
//!
//! match rl.check("key", rate_limit.clone(), 1).await {
//! match rl.check("key", &rate_limit, 1).await {
//! Err(GcraError::DeniedUntil { next_allowed_at }) => {
//! print!("Denied: Request next at {:?}", next_allowed_at);
//! Ok(())
Expand Down
62 changes: 21 additions & 41 deletions src/rate_limiter/rate_limiter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ where
pub async fn check(
&self,
key: Key,
rate_limit: RateLimit,
rate_limit: &RateLimit,
cost: u32,
) -> Result<Instant, GcraError> {
self.check_at(key, rate_limit, cost, self.clock.now()).await
Expand All @@ -96,16 +96,16 @@ where
pub async fn check_at(
&self,
key: Key,
rate_limit: RateLimit,
rate_limit: &RateLimit,
cost: u32,
arrived_at: Instant,
) -> Result<Instant, GcraError> {
let request_key = RateLimitRequest { key };

let mut entry = self.map.entry(request_key.clone()).or_default();
match entry.check_and_modify_at(&rate_limit, arrived_at, cost) {
match entry.check_and_modify_at(rate_limit, arrived_at, cost) {
Ok(_) => {
entry.update_expiration(&rate_limit);
entry.update_expiration(rate_limit);
// Guaranteed to be set from update_expiration
let expires_at = entry.expires_at.unwrap();
Ok(expires_at)
Expand Down Expand Up @@ -147,12 +147,12 @@ mod tests {

for _ in 0..rate_limit.resource_limit {
assert!(
rl.check("key", rate_limit.clone(), 1).await.is_ok(),
rl.check("key", &rate_limit, 1).await.is_ok(),
"Shouldn't be rate limited yet"
);
}

match rl.check("key", rate_limit, 1).await {
match rl.check("key", &rate_limit, 1).await {
Ok(_) => panic!("We should be rate limited"),
Err(GcraError::DeniedUntil { next_allowed_at }) => {
assert!(next_allowed_at > Instant::now())
Expand All @@ -166,7 +166,7 @@ mod tests {
let rate_limit = RateLimit::new(3, Duration::from_secs(3));
let rl = RateLimiter::with_shards(4, 2);

match rl.check("key", rate_limit.clone(), 9).await {
match rl.check("key", &rate_limit, 9).await {
Ok(_) => panic!("We should be rate limited"),
Err(GcraError::DeniedIndefinitely {
cost,
Expand All @@ -185,49 +185,29 @@ mod tests {
let rl = RateLimiter::with_shards(4, 2);

let now = Instant::now();
assert!(rl.check_at("key", rate_limit.clone(), 1, now).await.is_ok());
assert!(rl.check_at("key", &rate_limit, 1, now).await.is_ok());
assert!(
rl.check_at(
"key",
rate_limit.clone(),
1,
now + Duration::from_millis(250)
)
.await
.is_ok(),
rl.check_at("key", &rate_limit, 1, now + Duration::from_millis(250))
.await
.is_ok(),
"delay the 2nd check"
);
assert!(
rl.check_at(
"key",
rate_limit.clone(),
1,
now + Duration::from_millis(251)
)
.await
.is_err(),
rl.check_at("key", &rate_limit, 1, now + Duration::from_millis(251))
.await
.is_err(),
"check we are denied start"
);
assert!(
rl.check_at(
"key",
rate_limit.clone(),
1,
now + Duration::from_millis(499)
)
.await
.is_err(),
rl.check_at("key", &rate_limit, 1, now + Duration::from_millis(499))
.await
.is_err(),
"check we are denied end"
);
assert!(
rl.check_at(
"key",
rate_limit.clone(),
1,
now + Duration::from_millis(501)
)
.await
.is_ok(),
rl.check_at("key", &rate_limit, 1, now + Duration::from_millis(501))
.await
.is_ok(),
"1st use should be released"
)
}
Expand All @@ -241,7 +221,7 @@ mod tests {

for index in 0..rate_limit.resource_limit {
assert!(
rl.check(index, rate_limit.clone(), 1).await.is_ok(),
rl.check(index, &rate_limit, 1).await.is_ok(),
"Shouldn't be rate limited yet"
);
}
Expand Down

0 comments on commit 0c878b9

Please sign in to comment.