// benches/scan_ops.rs use criterion::{criterion_group, criterion_main, Criterion, BenchmarkId}; mod common; use common::*; /// Benchmark SCAN operation - full database scan fn bench_scan_full(c: &mut Criterion) { let mut group = c.benchmark_group("scan_ops/scan_full"); for size in [1_000, 10_000] { for backend_type in BackendType::all() { let backend = setup_populated_backend(backend_type, size, 100) .expect("Failed to setup backend"); group.bench_with_input( BenchmarkId::new(format!("{}/size", backend.name()), size), &backend, |b, backend| { b.iter(|| { let mut cursor = 0u64; let mut total = 0; loop { let (next_cursor, items) = backend.storage .scan(cursor, None, Some(100)) .unwrap(); total += items.len(); if next_cursor == 0 { break; } cursor = next_cursor; } total }); } ); } } group.finish(); } /// Benchmark SCAN operation with pattern matching fn bench_scan_pattern(c: &mut Criterion) { let mut group = c.benchmark_group("scan_ops/scan_pattern"); for backend_type in BackendType::all() { // Create backend with mixed key patterns let backend = BenchmarkBackend::new(backend_type).expect("Failed to create backend"); let mut generator = DataGenerator::new(42); // Insert keys with different patterns for i in 0..3_000 { let key = if i < 1_000 { format!("user:{}:profile", i) } else if i < 2_000 { format!("session:{}:data", i - 1_000) } else { format!("cache:{}:value", i - 2_000) }; let value = generator.generate_value(100); backend.storage.set(key, value).unwrap(); } // Benchmark pattern matching for pattern in ["user:*", "session:*", "cache:*"] { group.bench_with_input( BenchmarkId::new(format!("{}/pattern", backend.name()), pattern), &(backend.storage.clone(), pattern), |b, (storage, pattern)| { b.iter(|| { let mut cursor = 0u64; let mut total = 0; loop { let (next_cursor, items) = storage .scan(cursor, Some(pattern), Some(100)) .unwrap(); total += items.len(); if next_cursor == 0 { break; } cursor = next_cursor; } total }); } ); } } group.finish(); } /// Benchmark HSCAN operation - scan hash fields fn bench_hscan(c: &mut Criterion) { let mut group = c.benchmark_group("scan_ops/hscan"); for fields_count in [10, 100] { for backend_type in BackendType::all() { let backend = setup_populated_backend_hashes(backend_type, 100, fields_count, 100) .expect("Failed to setup backend"); let generator = DataGenerator::new(42); let key = generator.generate_key("bench:hash", 0); group.bench_with_input( BenchmarkId::new(format!("{}/fields", backend.name()), fields_count), &(backend, key), |b, (backend, key)| { b.iter(|| { let mut cursor = 0u64; let mut total = 0; loop { let (next_cursor, items) = backend.storage .hscan(key, cursor, None, Some(10)) .unwrap(); total += items.len(); if next_cursor == 0 { break; } cursor = next_cursor; } total }); } ); } } group.finish(); } /// Benchmark HSCAN with pattern matching fn bench_hscan_pattern(c: &mut Criterion) { let mut group = c.benchmark_group("scan_ops/hscan_pattern"); for backend_type in BackendType::all() { let backend = BenchmarkBackend::new(backend_type).expect("Failed to create backend"); let mut generator = DataGenerator::new(42); // Create a hash with mixed field patterns let key = "bench:hash:0".to_string(); let mut fields = Vec::new(); for i in 0..100 { let field = if i < 33 { format!("user_{}", i) } else if i < 66 { format!("session_{}", i - 33) } else { format!("cache_{}", i - 66) }; let value = generator.generate_value(100); fields.push((field, value)); } backend.storage.hset(&key, fields).unwrap(); // Benchmark pattern matching for pattern in ["user_*", "session_*", "cache_*"] { group.bench_with_input( BenchmarkId::new(format!("{}/pattern", backend.name()), pattern), &(backend.storage.clone(), key.clone(), pattern), |b, (storage, key, pattern)| { b.iter(|| { let mut cursor = 0u64; let mut total = 0; loop { let (next_cursor, items) = storage .hscan(key, cursor, Some(pattern), Some(10)) .unwrap(); total += items.len(); if next_cursor == 0 { break; } cursor = next_cursor; } total }); } ); } } group.finish(); } /// Benchmark KEYS operation with various patterns fn bench_keys_operation(c: &mut Criterion) { let mut group = c.benchmark_group("scan_ops/keys"); for backend_type in BackendType::all() { // Create backend with mixed key patterns let backend = BenchmarkBackend::new(backend_type).expect("Failed to create backend"); let mut generator = DataGenerator::new(42); // Insert keys with different patterns for i in 0..3_000 { let key = if i < 1_000 { format!("user:{}:profile", i) } else if i < 2_000 { format!("session:{}:data", i - 1_000) } else { format!("cache:{}:value", i - 2_000) }; let value = generator.generate_value(100); backend.storage.set(key, value).unwrap(); } // Benchmark different patterns for pattern in ["*", "user:*", "session:*", "*:profile", "user:*:profile"] { group.bench_with_input( BenchmarkId::new(format!("{}/pattern", backend.name()), pattern), &(backend.storage.clone(), pattern), |b, (storage, pattern)| { b.iter(|| { storage.keys(pattern).unwrap() }); } ); } } group.finish(); } /// Benchmark DBSIZE operation fn bench_dbsize(c: &mut Criterion) { let mut group = c.benchmark_group("scan_ops/dbsize"); for size in [1_000, 10_000] { for backend_type in BackendType::all() { let backend = setup_populated_backend(backend_type, size, 100) .expect("Failed to setup backend"); group.bench_with_input( BenchmarkId::new(format!("{}/size", backend.name()), size), &backend, |b, backend| { b.iter(|| { backend.storage.dbsize().unwrap() }); } ); } } group.finish(); } /// Benchmark LRANGE with different range sizes fn bench_lrange_sizes(c: &mut Criterion) { let mut group = c.benchmark_group("scan_ops/lrange"); for range_size in [10, 50, 100] { for backend_type in BackendType::all() { let backend = setup_populated_backend_lists(backend_type, 100, 100, 100) .expect("Failed to setup backend"); let generator = DataGenerator::new(42); let key = generator.generate_key("bench:list", 0); group.bench_with_input( BenchmarkId::new(format!("{}/range", backend.name()), range_size), &(backend, key, range_size), |b, (backend, key, range_size)| { b.iter(|| { backend.storage.lrange(key, 0, (*range_size - 1) as i64).unwrap() }); } ); } } group.finish(); } /// Benchmark HKEYS operation fn bench_hkeys(c: &mut Criterion) { let mut group = c.benchmark_group("scan_ops/hkeys"); for fields_count in [10, 50, 100] { for backend_type in BackendType::all() { let backend = setup_populated_backend_hashes(backend_type, 100, fields_count, 100) .expect("Failed to setup backend"); let generator = DataGenerator::new(42); let key = generator.generate_key("bench:hash", 0); group.bench_with_input( BenchmarkId::new(format!("{}/fields", backend.name()), fields_count), &(backend, key), |b, (backend, key)| { b.iter(|| { backend.storage.hkeys(key).unwrap() }); } ); } } group.finish(); } /// Benchmark HVALS operation fn bench_hvals(c: &mut Criterion) { let mut group = c.benchmark_group("scan_ops/hvals"); for fields_count in [10, 50, 100] { for backend_type in BackendType::all() { let backend = setup_populated_backend_hashes(backend_type, 100, fields_count, 100) .expect("Failed to setup backend"); let generator = DataGenerator::new(42); let key = generator.generate_key("bench:hash", 0); group.bench_with_input( BenchmarkId::new(format!("{}/fields", backend.name()), fields_count), &(backend, key), |b, (backend, key)| { b.iter(|| { backend.storage.hvals(key).unwrap() }); } ); } } group.finish(); } criterion_group!( benches, bench_scan_full, bench_scan_pattern, bench_hscan, bench_hscan_pattern, bench_keys_operation, bench_dbsize, bench_lrange_sizes, bench_hkeys, bench_hvals, ); criterion_main!(benches);