use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main}; use rustfs_notify::rules::RulesMap; use rustfs_targets::arn::TargetID; use starshard::{AsyncShardedHashMap, DEFAULT_SHARDS, SnapshotMode}; use tokio::runtime::Runtime; fn build_rule_map(target: &TargetID) -> RulesMap { let mut rules_map = RulesMap::new(); rules_map.add_rule_config(&[rustfs_s3_types::EventName::ObjectCreatedPut], "*".to_string(), target.clone()); rules_map } async fn build_map(mode: SnapshotMode, bucket_count: usize) -> AsyncShardedHashMap { let map = AsyncShardedHashMap::with_snapshot_mode(DEFAULT_SHARDS, mode); let target = TargetID::new("bench-target".to_string(), "webhook".to_string()); for i in 0..bucket_count { let bucket = format!("bucket-{i}"); map.insert(bucket, build_rule_map(&target)).await; } map } async fn scan_target_bound(map: &AsyncShardedHashMap, target_id: &TargetID) -> bool { let items = map.iter().await; for (_bucket, rules_map) in items { if rules_map.contains_target_id(target_id) { return true; } } false } fn bench_snapshot_mode_scan(c: &mut Criterion) { let rt = Runtime::new().expect("tokio runtime"); let mut group = c.benchmark_group("notify_rule_engine_scan_snapshot_mode"); // Simulate medium-large config sets where full-map scan cost matters. let bucket_sizes = [1_000usize, 10_000usize]; for bucket_count in bucket_sizes { for (mode, mode_name) in [(SnapshotMode::Clone, "clone"), (SnapshotMode::Cached, "cached")] { let map = rt.block_on(build_map(mode, bucket_count)); let miss_target = TargetID::new("missing-target".to_string(), "webhook".to_string()); group.throughput(Throughput::Elements(bucket_count as u64)); group.bench_with_input(BenchmarkId::new(mode_name, bucket_count), &bucket_count, |b, _| { b.iter(|| { let found = rt.block_on(async { scan_target_bound(std::hint::black_box(&map), std::hint::black_box(&miss_target)).await }); std::hint::black_box(found); }); }); } } group.finish(); } criterion_group!(benches, bench_snapshot_mode_scan); criterion_main!(benches);