#![allow(dead_code)]
use std::sync::Arc;
use trypema::{
BucketSize, HardLimitFactor, RateLimiterBuilder, SuppressionFactorCachePeriod, WindowSize,
local::LocalRateLimiterProvider,
};
pub const BENCH_PREFIX: &str = "bench";
pub struct LimiterConfig {
pub window_size: u64,
pub bucket_size: u64,
pub hard_limit_factor: f64,
pub suppression_factor_cache_period: u64,
pub prefix: &'static str,
}
impl Default for LimiterConfig {
fn default() -> Self {
Self {
window_size: 60,
bucket_size: 10,
hard_limit_factor: 1.0,
suppression_factor_cache_period: 100,
prefix: BENCH_PREFIX,
}
}
}
pub fn build_local_limiter(config: LimiterConfig) -> Arc<LocalRateLimiterProvider> {
LocalRateLimiterProvider::builder()
.window_size(WindowSize::seconds_or_panic(config.window_size))
.bucket_size(BucketSize::milliseconds_or_panic(config.bucket_size))
.hard_limit_factor(HardLimitFactor::new_or_panic(config.hard_limit_factor))
.suppression_factor_cache_period(SuppressionFactorCachePeriod::milliseconds_or_panic(
config.suppression_factor_cache_period,
))
.cleanup_enabled(false)
.build()
.unwrap()
}
pub fn measure_parallel<F>(
iterations: u64,
thread_count: usize,
operation: F,
) -> std::time::Duration
where
F: Fn() + Sync,
{
use std::{
sync::Barrier,
thread,
time::{Duration, Instant},
};
assert!(thread_count > 0, "thread_count must be greater than zero");
let ready = Barrier::new(thread_count + 1);
let start = Barrier::new(thread_count + 1);
thread::scope(|scope| {
let operation = &operation;
let mut handles = Vec::with_capacity(thread_count);
for worker_index in 0..thread_count {
let base_iterations = iterations / thread_count as u64;
let remainder = iterations % thread_count as u64;
let worker_iterations = base_iterations + u64::from((worker_index as u64) < remainder);
let ready = &ready;
let start = &start;
handles.push(scope.spawn(move || {
ready.wait();
start.wait();
for _ in 0..worker_iterations {
operation();
}
}));
}
ready.wait();
let started = Instant::now();
start.wait();
for handle in handles {
handle.join().expect("benchmark worker panicked");
}
let elapsed = started.elapsed();
if iterations == 0 {
Duration::ZERO
} else {
elapsed
}
})
}
#[cfg(any(feature = "redis-tokio", feature = "redis-smol"))]
pub mod redis {
use std::{env, sync::Arc};
use trypema::{
RateLimiterBuilder,
hybrid::{HybridRateLimiterProvider, SyncInterval},
redis::{RedisKey, RedisRateLimiterProvider},
};
use super::LimiterConfig;
pub fn redis_url() -> String {
env::var("REDIS_URL").unwrap_or_else(|_| "redis://127.0.0.1:16379/".to_string())
}
async fn connection_manager() -> ::redis::aio::ConnectionManager {
let client = ::redis::Client::open(redis_url()).unwrap();
client.get_connection_manager().await.unwrap()
}
pub async fn build_redis_limiter(cfg: LimiterConfig) -> Arc<RedisRateLimiterProvider> {
let prefix = RedisKey::try_from(cfg.prefix.to_string()).unwrap();
RedisRateLimiterProvider::builder(connection_manager().await)
.prefix(prefix)
.window_size(super::WindowSize::seconds_or_panic(cfg.window_size))
.bucket_size(super::BucketSize::milliseconds_or_panic(cfg.bucket_size))
.hard_limit_factor(super::HardLimitFactor::new_or_panic(cfg.hard_limit_factor))
.suppression_factor_cache_period(
super::SuppressionFactorCachePeriod::milliseconds_or_panic(
cfg.suppression_factor_cache_period,
),
)
.cleanup_enabled(false)
.build()
.unwrap()
}
pub async fn build_hybrid_limiter(cfg: LimiterConfig) -> Arc<HybridRateLimiterProvider> {
let prefix = RedisKey::try_from(cfg.prefix.to_string()).unwrap();
HybridRateLimiterProvider::builder(connection_manager().await)
.prefix(prefix)
.window_size(super::WindowSize::seconds_or_panic(cfg.window_size))
.bucket_size(super::BucketSize::milliseconds_or_panic(cfg.bucket_size))
.hard_limit_factor(super::HardLimitFactor::new_or_panic(cfg.hard_limit_factor))
.suppression_factor_cache_period(
super::SuppressionFactorCachePeriod::milliseconds_or_panic(
cfg.suppression_factor_cache_period,
),
)
.sync_interval(SyncInterval::default())
.cleanup_enabled(false)
.build()
.unwrap()
}
}