use matchy::{DataValue, Database, DatabaseBuilder, MatchMode};
use std::collections::HashMap;
use std::time::Instant;
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== Matchy Query Caching Demo ===\n");
println!("1. Building test database...");
let mut builder = DatabaseBuilder::new(MatchMode::CaseSensitive)
.with_database_type("Cache-Demo")
.with_description("en", "Caching demonstration database");
let mut data = HashMap::new();
data.insert("type".to_string(), DataValue::String("test".to_string()));
for i in 0..100 {
builder.add_ip(&format!("10.0.0.{i}"), data.clone())?;
}
for i in 0..100 {
builder.add_glob(&format!("*.pattern{i}.com"), data.clone())?;
}
let db_bytes = builder.build()?;
println!(" Database size: {} bytes\n", db_bytes.len());
println!("2. Testing WITHOUT caching (baseline)...");
let db_uncached = Database::from_bytes_builder(db_bytes.clone())
.no_cache() .open()?;
let queries = generate_queries(10_000, 50); let start = Instant::now();
for query in &queries {
let _ = db_uncached.lookup(query)?;
}
let uncached_duration = start.elapsed();
let uncached_qps = queries.len() as f64 / uncached_duration.as_secs_f64();
println!(" Queries: {}", queries.len());
println!(" Duration: {:.3}s", uncached_duration.as_secs_f64());
println!(" QPS: {uncached_qps:.0}\n");
println!("3. Testing WITH caching (10k capacity)...");
let db_cached = Database::from_bytes_builder(db_bytes.clone())
.cache_capacity(10_000) .open()?;
let start = Instant::now();
for query in &queries {
let _ = db_cached.lookup(query)?;
}
let cached_duration = start.elapsed();
let cached_qps = queries.len() as f64 / cached_duration.as_secs_f64();
println!(" Queries: {}", queries.len());
println!(" Duration: {:.3}s", cached_duration.as_secs_f64());
println!(" QPS: {cached_qps:.0}");
let speedup = uncached_duration.as_secs_f64() / cached_duration.as_secs_f64();
println!(" Speedup: {speedup:.2}x faster\n");
println!("4. Cache management...");
println!(" Cache entries before clear: {}", db_cached.cache_size());
db_cached.clear_cache();
println!(
" Cache entries after clear: {}\n",
db_cached.cache_size()
);
println!("5. Performance at different cache hit rates:");
println!(" Hit Rate | QPS | vs Uncached");
println!(" ---------|------------|------------");
for hit_rate in [0, 50, 80, 95, 99] {
let db = Database::from_bytes_builder(db_bytes.clone())
.cache_capacity(10_000)
.open()?;
let queries = generate_queries(5_000, hit_rate);
db.clear_cache();
let start = Instant::now();
for query in &queries {
let _ = db.lookup(query)?;
}
let duration = start.elapsed();
let qps = queries.len() as f64 / duration.as_secs_f64();
let speedup = qps / uncached_qps;
println!(" {hit_rate:>3}% | {qps:>10.0} | {speedup:>6.2}x");
}
println!("\n=== Key Takeaways ===");
println!("✓ Caching provides 2-10x speedup at high hit rates (80%+)");
println!("✓ Zero overhead when disabled (.no_cache())");
println!("✓ Thread-safe and memory-efficient (LRU eviction)");
println!("✓ Best for: web APIs, firewalls, real-time threat detection");
println!("✓ Skip for: batch processing, one-time scans");
Ok(())
}
fn generate_queries(count: usize, hit_rate: usize) -> Vec<String> {
let unique = if hit_rate == 0 {
count } else {
(count * (100 - hit_rate)) / 100
};
(0..count)
.map(|i| {
let idx = i % unique.max(1);
match idx % 2 {
0 => format!("10.0.0.{}", idx % 100),
_ => format!("test.pattern{}.com", idx % 100),
}
})
.collect()
}