use processor::state::{
CacheConfig, CachedStateBackend, EvictionPolicy, MemoryStateBackend, StateBackend,
WriteStrategy,
};
use std::time::Duration;
use tracing::{info, Level};
use tracing_subscriber::FmtSubscriber;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let subscriber = FmtSubscriber::builder()
.with_max_level(Level::INFO)
.finish();
tracing::subscriber::set_global_default(subscriber)?;
info!("=== Cached State Backend Demo ===\n");
demo_write_through().await?;
demo_write_back().await?;
demo_cache_warming().await?;
demo_batch_operations().await?;
demo_statistics().await?;
demo_eviction_policies().await?;
demo_invalidation().await?;
info!("\n=== Demo Complete ===");
Ok(())
}
async fn demo_write_through() -> anyhow::Result<()> {
info!("--- Demo 1: Write-Through Strategy ---");
let backend = MemoryStateBackend::new(None);
let config = CacheConfig {
write_strategy: WriteStrategy::WriteThrough,
l1_capacity: 1000,
l1_ttl: Duration::from_secs(300),
l2_ttl: Duration::from_secs(3600),
enable_stats: true,
..Default::default()
};
let cached = CachedStateBackend::new(backend.clone(), None, config).await?;
info!("Writing data with write-through strategy...");
cached.put(b"user:123", b"Alice").await?;
cached.put(b"user:456", b"Bob").await?;
let backend_value = backend.get(b"user:123").await?;
info!("Backend contains: {:?}", backend_value);
let value = cached.get(b"user:123").await?;
info!("Retrieved from cache: {:?}", value);
let stats = cached.stats().await;
info!(
"L1 hit rate: {:.2}%, Avg latency: {:.2}μs\n",
stats.l1_hit_rate * 100.0,
stats.l1_avg_latency_us
);
Ok(())
}
async fn demo_write_back() -> anyhow::Result<()> {
info!("--- Demo 2: Write-Back Strategy ---");
let backend = MemoryStateBackend::new(None);
let config = CacheConfig {
write_strategy: WriteStrategy::WriteBack,
l1_capacity: 1000,
enable_stats: true,
..Default::default()
};
let cached = CachedStateBackend::new(backend.clone(), None, config).await?;
info!("Writing data with write-back strategy...");
let start = std::time::Instant::now();
for i in 0..100 {
let key = format!("session:{}", i);
let value = format!("session_data_{}", i);
cached.put(key.as_bytes(), value.as_bytes()).await?;
}
let elapsed = start.elapsed();
info!("Wrote 100 entries in {:?}", elapsed);
tokio::time::sleep(Duration::from_millis(200)).await;
let backend_count = backend.count().await?;
info!("Backend now contains {} entries", backend_count);
let stats = cached.stats().await;
info!(
"Write throughput: {:.0} ops/sec\n",
100.0 / elapsed.as_secs_f64()
);
Ok(())
}
async fn demo_cache_warming() -> anyhow::Result<()> {
info!("--- Demo 3: Cache Warming ---");
let backend = MemoryStateBackend::new(None);
info!("Prepopulating backend with 1000 entries...");
for i in 0..1000 {
let key = format!("product:{}", i);
let value = format!("product_data_{}", i);
backend.put(key.as_bytes(), value.as_bytes()).await?;
}
let config = CacheConfig {
l1_capacity: 100, enable_stats: true,
..Default::default()
};
let cached = CachedStateBackend::new(backend, None, config).await?;
let hot_keys: Vec<Vec<u8>> = (0..10)
.map(|i| format!("product:{}", i).into_bytes())
.collect();
info!("Warming cache with {} hot keys...", hot_keys.len());
cached.warm_cache(&hot_keys).await?;
let start = std::time::Instant::now();
for key in &hot_keys {
cached.get(key).await?;
}
let hot_elapsed = start.elapsed();
let start = std::time::Instant::now();
for i in 90..100 {
let key = format!("product:{}", i);
cached.get(key.as_bytes()).await?;
}
let cold_elapsed = start.elapsed();
let stats = cached.stats().await;
info!(
"Hot keys (L1): {:?}, Cold keys (L3): {:?}",
hot_elapsed, cold_elapsed
);
info!(
"L1 hits: {}, L3 hits: {}\n",
stats.l1_hits, stats.l3_hits
);
Ok(())
}
async fn demo_batch_operations() -> anyhow::Result<()> {
info!("--- Demo 4: Batch Operations ---");
let backend = MemoryStateBackend::new(None);
let config = CacheConfig {
l1_capacity: 10000,
enable_stats: true,
..Default::default()
};
let cached = CachedStateBackend::new(backend, None, config).await?;
info!("Performing batch put...");
let pairs: Vec<_> = (0..1000)
.map(|i| {
(
format!("key:{}", i).into_bytes(),
format!("value:{}", i).into_bytes(),
)
})
.collect();
let start = std::time::Instant::now();
cached.put_batch(&pairs).await?;
let put_elapsed = start.elapsed();
info!("Performing batch get...");
let keys: Vec<_> = (0..1000)
.map(|i| format!("key:{}", i).into_bytes())
.collect();
let start = std::time::Instant::now();
let values = cached.get_batch(&keys).await?;
let get_elapsed = start.elapsed();
info!(
"Batch put: {:?} ({:.0} ops/sec)",
put_elapsed,
1000.0 / put_elapsed.as_secs_f64()
);
info!(
"Batch get: {:?} ({:.0} ops/sec)",
get_elapsed,
1000.0 / get_elapsed.as_secs_f64()
);
info!("Retrieved {} values\n", values.len());
Ok(())
}
async fn demo_statistics() -> anyhow::Result<()> {
info!("--- Demo 5: Statistics Monitoring ---");
let backend = MemoryStateBackend::new(None);
for i in 0..100 {
let key = format!("metric:{}", i);
let value = format!("data:{}", i);
backend.put(key.as_bytes(), value.as_bytes()).await?;
}
let config = CacheConfig {
l1_capacity: 50, enable_stats: true,
..Default::default()
};
let cached = CachedStateBackend::new(backend, None, config).await?;
info!("Simulating workload...");
for _ in 0..5 {
for i in 0..20 {
let key = format!("metric:{}", i);
cached.get(key.as_bytes()).await?;
}
}
for i in 20..50 {
let key = format!("metric:{}", i);
cached.get(key.as_bytes()).await?;
}
let stats = cached.stats().await;
info!("\n=== Cache Statistics ===");
info!("L1 Cache:");
info!(" Hits: {}", stats.l1_hits);
info!(" Misses: {}", stats.l1_misses);
info!(" Hit Rate: {:.2}%", stats.l1_hit_rate * 100.0);
info!(" Avg Latency: {:.2}μs", stats.l1_avg_latency_us);
info!(" Size: {} / {}", stats.l1_size, stats.l1_capacity);
info!("\nL3 Backend:");
info!(" Hits: {}", stats.l3_hits);
info!(" Misses: {}", stats.l3_misses);
info!(" Hit Rate: {:.2}%", stats.l3_hit_rate * 100.0);
info!(" Avg Latency: {:.2}μs", stats.l3_avg_latency_us);
info!("\nOverall:");
info!(" Hit Rate: {:.2}%", stats.overall_hit_rate * 100.0);
info!(" Total Writes: {}", stats.writes_total);
info!("");
Ok(())
}
async fn demo_eviction_policies() -> anyhow::Result<()> {
info!("--- Demo 6: Eviction Policies ---");
for policy in [EvictionPolicy::LRU, EvictionPolicy::LFU, EvictionPolicy::FIFO].iter() {
info!("\nTesting {:?} policy:", policy);
let backend = MemoryStateBackend::new(None);
let config = CacheConfig {
l1_capacity: 10,
eviction_policy: *policy,
enable_stats: true,
..Default::default()
};
let cached = CachedStateBackend::new(backend, None, config).await?;
for i in 0..10 {
let key = format!("item:{}", i);
let value = format!("data:{}", i);
cached.put(key.as_bytes(), value.as_bytes()).await?;
}
cached.get(b"item:0").await?;
cached.get(b"item:1").await?;
cached.get(b"item:0").await?;
cached.put(b"item:new", b"new_data").await?;
let stats = cached.stats().await;
info!(" Cache size: {}", stats.l1_size);
info!(" Evictions: {}", stats.l1_evictions);
}
info!("");
Ok(())
}
async fn demo_invalidation() -> anyhow::Result<()> {
info!("--- Demo 7: Cache Invalidation ---");
let backend = MemoryStateBackend::new(None);
let config = CacheConfig {
l1_capacity: 100,
enable_stats: true,
..Default::default()
};
let cached = CachedStateBackend::new(backend.clone(), None, config).await?;
info!("Adding data to cache...");
for i in 0..10 {
let key = format!("config:{}", i);
let value = format!("value:{}", i);
cached.put(key.as_bytes(), value.as_bytes()).await?;
}
cached.get(b"config:5").await?;
let stats = cached.stats().await;
info!("L1 hits before invalidation: {}", stats.l1_hits);
info!("Invalidating config:5...");
cached.invalidate(b"config:5").await?;
cached.get(b"config:5").await?;
let stats = cached.stats().await;
info!("L3 hits after invalidation: {}", stats.l3_hits);
info!("Clearing entire cache...");
cached.clear_cache().await?;
for i in 0..10 {
let key = format!("config:{}", i);
cached.get(key.as_bytes()).await?;
}
let stats = cached.stats().await;
info!("L3 hits after cache clear: {}", stats.l3_hits);
info!("Cache still contains data in backend: {}", backend.count().await?);
info!("");
Ok(())
}