#![allow(dead_code, unused_variables, clippy::manual_range_contains, clippy::clone_on_copy)] //! Authorization Service Performance Benchmark //! //! Compares performance between: //! - RwLock (baseline) //! - DashMap (optimized) //! //! Target: <8ns per RBAC check (12x improvement from ~100ns baseline) use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use dashmap::DashMap; use std::collections::{HashMap, HashSet}; use std::sync::Arc; use std::time::Instant; use tokio::sync::RwLock; use uuid::Uuid; /// User permissions structure #[derive(Clone, Debug)] struct UserPermissions { user_id: Uuid, permissions: HashSet, loaded_at: Instant, } /// RwLock-based cache (baseline) struct RwLockAuthzCache { cache: Arc>>, } impl RwLockAuthzCache { fn new() -> Self { Self { cache: Arc::new(RwLock::new(HashMap::new())), } } async fn check_permission(&self, user_id: &Uuid, endpoint: &str) -> bool { let cache = self.cache.read().await; if let Some(user_perms) = cache.get(user_id) { user_perms.permissions.contains(endpoint) } else { false } } async fn insert(&self, user_id: Uuid, perms: UserPermissions) { let mut cache = self.cache.write().await; cache.insert(user_id, perms); } } /// DashMap-based cache (optimized) struct DashMapAuthzCache { cache: Arc>, } impl DashMapAuthzCache { fn new() -> Self { Self { cache: Arc::new(DashMap::new()), } } fn check_permission(&self, user_id: &Uuid, endpoint: &str) -> bool { if let Some(user_perms_ref) = self.cache.get(user_id) { user_perms_ref.permissions.contains(endpoint) } else { false } } fn insert(&self, user_id: Uuid, perms: UserPermissions) { self.cache.insert(user_id, perms); } } /// Benchmark: RwLock cache read (baseline) fn bench_rwlock_read(c: &mut Criterion) { let rt = tokio::runtime::Runtime::new().unwrap(); let cache = RwLockAuthzCache::new(); // Prepopulate with 1000 users rt.block_on(async { for i in 0..1000 { let user_id = Uuid::new_v4(); let perms = UserPermissions { user_id, permissions: vec![ "/api/trade".to_string(), "/api/portfolio".to_string(), "/api/risk".to_string(), ] .into_iter() .collect(), loaded_at: Instant::now(), }; cache.insert(user_id, perms).await; } }); // Get a user ID for benchmarking let test_user_id = rt.block_on(async { let cache_guard = cache.cache.read().await; cache_guard.keys().next().copied().unwrap() }); c.bench_function("rwlock_permission_check", |b| { b.iter_custom(|iters| { let start = Instant::now(); rt.block_on(async { for _ in 0..iters { let result = cache .check_permission(black_box(&test_user_id), black_box("/api/trade")) .await; black_box(result); } }); start.elapsed() }) }); } /// Benchmark: DashMap cache read (optimized) fn bench_dashmap_read(c: &mut Criterion) { let cache = DashMapAuthzCache::new(); // Prepopulate with 1000 users for i in 0..1000 { let user_id = Uuid::new_v4(); let perms = UserPermissions { user_id, permissions: vec![ "/api/trade".to_string(), "/api/portfolio".to_string(), "/api/risk".to_string(), ] .into_iter() .collect(), loaded_at: Instant::now(), }; cache.insert(user_id, perms); } // Get a user ID for benchmarking let test_user_id = cache.cache.iter().next().expect("INVARIANT: Iterator should have next element").key().clone(); c.bench_function("dashmap_permission_check", |b| { b.iter(|| { let result = cache.check_permission(black_box(&test_user_id), black_box("/api/trade")); black_box(result); }); }); } /// Benchmark: Compare different cache sizes fn bench_cache_sizes(c: &mut Criterion) { let mut group = c.benchmark_group("authz_cache_sizes"); for size in &[100, 1_000, 10_000, 100_000] { // DashMap benchmark let dashmap_cache = DashMapAuthzCache::new(); let mut test_user_ids = Vec::new(); for i in 0..*size { let user_id = Uuid::new_v4(); test_user_ids.push(user_id); let perms = UserPermissions { user_id, permissions: vec!["/api/trade".to_string(), "/api/portfolio".to_string()] .into_iter() .collect(), loaded_at: Instant::now(), }; dashmap_cache.insert(user_id, perms); } let mid_user = test_user_ids[size / 2]; group.bench_with_input(BenchmarkId::new("dashmap", size), size, |b, _| { b.iter(|| { let result = dashmap_cache.check_permission(black_box(&mid_user), black_box("/api/trade")); black_box(result); }); }); } group.finish(); } /// Benchmark: Concurrent reads fn bench_concurrent_reads(c: &mut Criterion) { let mut group = c.benchmark_group("concurrent_reads"); // DashMap - lock-free concurrent reads let dashmap_cache = Arc::new(DashMapAuthzCache::new()); let mut test_user_ids = Vec::new(); for i in 0..1000 { let user_id = Uuid::new_v4(); test_user_ids.push(user_id); let perms = UserPermissions { user_id, permissions: vec!["/api/trade".to_string()].into_iter().collect(), loaded_at: Instant::now(), }; dashmap_cache.insert(user_id, perms); } group.bench_function("dashmap_concurrent_8_threads", |b| { b.iter(|| { let rt = tokio::runtime::Runtime::new().unwrap(); let cache = Arc::clone(&dashmap_cache); rt.block_on(async { let mut handles = Vec::new(); for i in 0..8 { let cache_clone = Arc::clone(&cache); let user_id = test_user_ids[i * 100]; let handle = tokio::spawn(async move { for _ in 0..100 { let result = cache_clone.check_permission(&user_id, "/api/trade"); black_box(result); } }); handles.push(handle); } for handle in handles { handle.await.unwrap(); } }); }); }); group.finish(); } /// Benchmark: Hot path - permission check only fn bench_hot_path(c: &mut Criterion) { let dashmap_cache = DashMapAuthzCache::new(); // Prepopulate with realistic data let mut test_users = Vec::new(); for i in 0..100 { let user_id = Uuid::new_v4(); test_users.push(user_id); let perms = UserPermissions { user_id, permissions: vec![ "/api/trade".to_string(), "/api/portfolio".to_string(), "/api/risk".to_string(), "/api/market-data".to_string(), ] .into_iter() .collect(), loaded_at: Instant::now(), }; dashmap_cache.insert(user_id, perms); } let hot_user = test_users[50]; c.bench_function("hot_path_permission_check", |b| { b.iter(|| { // Simulate typical RBAC check pattern let has_trade = dashmap_cache.check_permission(black_box(&hot_user), black_box("/api/trade")); let has_portfolio = dashmap_cache.check_permission(black_box(&hot_user), black_box("/api/portfolio")); black_box((has_trade, has_portfolio)); }); }); } /// Benchmark: Cache invalidation fn bench_cache_invalidation(c: &mut Criterion) { let mut group = c.benchmark_group("cache_invalidation"); // DashMap invalidation let dashmap_cache = DashMapAuthzCache::new(); for i in 0..1000 { let user_id = Uuid::new_v4(); let perms = UserPermissions { user_id, permissions: vec!["/api/trade".to_string()].into_iter().collect(), loaded_at: Instant::now(), }; dashmap_cache.insert(user_id, perms); } let test_user = dashmap_cache.cache.iter().next().expect("INVARIANT: Iterator should have next element").key().clone(); group.bench_function("dashmap_remove", |b| { b.iter(|| { dashmap_cache.cache.remove(black_box(&test_user)); // Re-insert for next iteration let perms = UserPermissions { user_id: test_user, permissions: vec!["/api/trade".to_string()].into_iter().collect(), loaded_at: Instant::now(), }; dashmap_cache.insert(test_user, perms); }); }); group.bench_function("dashmap_clear_all", |b| { b.iter(|| { dashmap_cache.cache.clear(); // Repopulate for next iteration for i in 0..100 { let user_id = Uuid::new_v4(); let perms = UserPermissions { user_id, permissions: vec!["/api/trade".to_string()].into_iter().collect(), loaded_at: Instant::now(), }; dashmap_cache.insert(user_id, perms); } }); }); group.finish(); } criterion_group!( authz_benches, bench_rwlock_read, bench_dashmap_read, bench_cache_sizes, bench_concurrent_reads, bench_hot_path, bench_cache_invalidation ); criterion_main!(authz_benches);