multi-tier-cache 0.6.5

Customizable multi-tier cache with L1 (Moka in-memory) + L2 (Redis distributed) defaults, expandable to L3/L4+, cross-instance invalidation via Pub/Sub, stampede protection, and flexible TTL scaling
Documentation
//! Benchmarks for cache invalidation operations

use criterion::{Criterion, black_box, criterion_group, criterion_main};
use multi_tier_cache::{
    Bytes, CacheManager, CacheStrategy, InvalidationConfig, L1Cache, L2Cache, MokaCacheConfig,
};
// use multi_tier_cache::error::CacheResult;
use serde_json::json;
use std::sync::Arc;
use std::time::Duration;
use tokio::runtime::Runtime;

fn setup_cache_with_invalidation() -> (Arc<CacheManager>, Runtime) {
    let rt = Runtime::new().unwrap_or_else(|_| panic!("Failed to create runtime"));
    let cache = rt.block_on(async {
        let redis_url =
            std::env::var("REDIS_URL").unwrap_or_else(|_| "redis://127.0.0.1:6379".to_string());

        let l1 = Arc::new(
            L1Cache::new(MokaCacheConfig::default())
                .unwrap_or_else(|_| panic!("Failed to create L1")),
        );
        let l2 = Arc::new(
            L2Cache::new()
                .await
                .unwrap_or_else(|_| panic!("Failed to create L2")),
        );
        let config = InvalidationConfig::default();

        Arc::new(
            CacheManager::new_with_invalidation(l1, l2, &redis_url, config)
                .await
                .unwrap_or_else(|_| panic!("Failed to create cache manager with invalidation")),
        )
    });
    (cache, rt)
}

/// Benchmark single key invalidation
fn bench_invalidate_single_key(c: &mut Criterion) {
    let (cache, rt) = setup_cache_with_invalidation();

    // Pre-populate cache
    rt.block_on(async {
        for i in 0..100 {
            let key = format!("bench:inv:{i}");
            let val = Bytes::from(
                serde_json::to_vec(&json!({"id": i}))
                    .unwrap_or_else(|e| panic!("Failed to serialize test data: {e}")),
            );
            cache
                .set_with_strategy(&key, val, CacheStrategy::MediumTerm)
                .await
                .unwrap_or_else(|_| panic!("Failed to set cache"));
        }
    });

    c.bench_function("invalidate_single_key", |b| {
        b.iter(|| {
            rt.block_on(async {
                let key = format!("bench:inv:{}", rand::random::<u8>() % 100);
                let _: () = cache
                    .invalidate(&key)
                    .await
                    .unwrap_or_else(|_| panic!("Failed to invalidate"));
                black_box(());
            });
        });
    });
}

/// Benchmark update cache operation
fn bench_update_cache(c: &mut Criterion) {
    let (cache, rt) = setup_cache_with_invalidation();

    rt.block_on(async {
        for i in 0..100 {
            let key = format!("bench:upd:{i}");
            let val = Bytes::from(
                serde_json::to_vec(&json!({"id": i}))
                    .unwrap_or_else(|e| panic!("Failed to serialize test data: {e}")),
            );
            cache
                .set_with_strategy(&key, val, CacheStrategy::MediumTerm)
                .await
                .unwrap_or_else(|_| panic!("Failed to set cache"));
        }
    });

    c.bench_function("update_cache", |b| {
        b.iter(|| {
            rt.block_on(async {
                let key = format!("bench:upd:{}", rand::random::<u8>() % 100);
                let new_value = Bytes::from(
                    serde_json::to_vec(&json!({"id": 999, "value": "updated"}))
                        .unwrap_or_else(|e| panic!("Failed to serialize test data: {e}")),
                );
                let _: () = cache
                    .update_cache(&key, new_value, Some(Duration::from_secs(300)))
                    .await
                    .unwrap_or_else(|_| panic!("Failed to update"));
                black_box(());
            });
        });
    });
}

criterion_group!(benches, bench_invalidate_single_key, bench_update_cache);
criterion_main!(benches);