use crate::data::enums::ratelimit::{BnRateLimit, BnRateLimitInterval, BnRateLimitType};
use governor::{DefaultDirectRateLimiter, InsufficientCapacity, Quota};
use std::{collections::HashMap, num::NonZero, time::Duration};
use xapi_shared::ratelimiter::SharedRatelimiterTrait;
pub struct BnRatelimiter {
limiters: HashMap<BnRateLimitType, Vec<DefaultDirectRateLimiter>>,
}
#[async_trait::async_trait]
impl SharedRatelimiterTrait<BnRateLimitType> for BnRatelimiter {
async fn limit_on(
&self,
key: &BnRateLimitType,
value: NonZero<u32>,
) -> Result<(), InsufficientCapacity> {
if let Some(limiters) = self.limiters.get(key) {
for limiter in limiters {
limiter
.until_n_ready(value)
.await
.inspect_err(|err| tracing::error!("bn rate limiter error: {err}"))?
}
}
Ok(())
}
}
impl Default for BnRatelimiter {
fn default() -> Self {
let limits = vec![
BnRateLimit {
rate_limit_type: BnRateLimitType::RequestWeight,
interval: BnRateLimitInterval::Minute,
interval_num: 1,
limit: 6000,
},
BnRateLimit {
rate_limit_type: BnRateLimitType::Orders,
interval: BnRateLimitInterval::Second,
interval_num: 10,
limit: 100,
},
BnRateLimit {
rate_limit_type: BnRateLimitType::Orders,
interval: BnRateLimitInterval::Day,
interval_num: 1,
limit: 200_000,
},
BnRateLimit {
rate_limit_type: BnRateLimitType::RawRequests,
interval: BnRateLimitInterval::Minute,
interval_num: 5,
limit: 61_000,
},
];
Self::new(&limits)
}
}
impl BnRatelimiter {
pub fn new(rules: &Vec<BnRateLimit>) -> Self {
let mut limiters = HashMap::new();
for rule in rules {
if rule.limit == 0 {
tracing::warn!("skipping rate limit rule with non-positive limit: {rule:?}");
continue;
}
let limit = NonZero::new(rule.limit).unwrap();
let quota = match rule.interval {
BnRateLimitInterval::Second => Quota::per_second(limit),
BnRateLimitInterval::Minute => Quota::per_minute(limit),
BnRateLimitInterval::Day => Quota::with_period(Duration::from_secs(60 * 60 * 24))
.unwrap()
.allow_burst(limit),
};
let limiter = DefaultDirectRateLimiter::direct(quota);
limiters
.entry(rule.rate_limit_type)
.or_insert_with(Vec::new)
.push(limiter);
}
Self { limiters }
}
}