use cached::macros::cached;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Barrier};
use std::thread;
use std::time::Duration;
static BY_KEY_CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
#[cached(sync_writes = "by_key")]
fn slow_lookup(key: u32) -> String {
BY_KEY_CALL_COUNT.fetch_add(1, Ordering::SeqCst);
thread::sleep(Duration::from_millis(50));
format!("value-for-{key}")
}
fn demo_sync_writes_by_key() {
println!("\n--- 1. sync_writes = \"by_key\" ---");
BY_KEY_CALL_COUNT.store(0, Ordering::SeqCst);
{
use cached::Cached;
SLOW_LOOKUP.write().cache_clear();
}
let barrier = Arc::new(Barrier::new(5));
let handles: Vec<_> = (0..5)
.map(|_| {
let b = Arc::clone(&barrier);
thread::spawn(move || {
b.wait(); slow_lookup(42)
})
})
.collect();
let results: Vec<_> = handles.into_iter().map(|h| h.join().unwrap()).collect();
let body_runs = BY_KEY_CALL_COUNT.load(Ordering::SeqCst);
println!(
" 5 concurrent calls for key=42: body ran {body_runs} time(s), \
all returned '{}'",
results[0]
);
assert_eq!(
body_runs, 1,
"body must run exactly once for 5 concurrent same-key calls"
);
assert!(
results.iter().all(|r| r == &results[0]),
"all callers must receive the same value"
);
BY_KEY_CALL_COUNT.store(0, Ordering::SeqCst);
let barrier2 = Arc::new(Barrier::new(3));
let distinct_handles: Vec<_> = [10u32, 20, 30]
.into_iter()
.map(|key| {
let b = Arc::clone(&barrier2);
thread::spawn(move || {
b.wait();
slow_lookup(key)
})
})
.collect();
for h in distinct_handles {
h.join().unwrap();
}
let distinct_runs = BY_KEY_CALL_COUNT.load(Ordering::SeqCst);
println!(
" 3 concurrent calls for distinct keys (10, 20, 30): body ran {distinct_runs} time(s)"
);
assert_eq!(distinct_runs, 3, "each distinct key must run the body once");
println!(" PASS: by_key deduplication confirmed");
}
static FALLBACK_SHOULD_FAIL: std::sync::atomic::AtomicBool =
std::sync::atomic::AtomicBool::new(false);
static FALLBACK_CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
#[cached(ttl_secs = 60, result_fallback = true)]
fn fetch_config() -> Result<String, String> {
FALLBACK_CALL_COUNT.fetch_add(1, Ordering::SeqCst);
if FALLBACK_SHOULD_FAIL.load(Ordering::SeqCst) {
Err("upstream unavailable".to_string())
} else {
Ok("config-v1".to_string())
}
}
fn demo_result_fallback() {
println!("\n--- 2. result_fallback = true ---");
FALLBACK_SHOULD_FAIL.store(false, Ordering::SeqCst);
FALLBACK_CALL_COUNT.store(0, Ordering::SeqCst);
let first = fetch_config();
assert_eq!(
first,
Ok("config-v1".to_string()),
"first call must succeed"
);
println!(" First call (Ok): {:?}", first);
FALLBACK_SHOULD_FAIL.store(true, Ordering::SeqCst);
let second = fetch_config();
assert_eq!(
second,
Ok("config-v1".to_string()),
"cache hit must return stale Ok"
);
println!(" Second call (cache hit, no recompute): {:?}", second);
{
use cached::Cached;
FETCH_CONFIG.write().cache_clear();
}
let third = fetch_config();
assert!(
third.is_err(),
"Err with no prior Ok in cache must propagate"
);
println!(
" Third call (cache cleared, no fallback available): {:?}",
third
);
{
use cached::Cached;
FETCH_CONFIG.write().cache_set((), "config-v2".to_string());
}
let fourth = fetch_config();
assert_eq!(
fourth,
Ok("config-v2".to_string()),
"Err with prior Ok in cache must serve stale Ok"
);
println!(" Fourth call (Err, falls back to stale Ok): {:?}", fourth);
println!(" PASS: result_fallback confirmed");
}
static REFRESH_CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
#[cached(
key = "u32",
convert = { id },
force_refresh = { bypass }
)]
fn get_value(id: u32, bypass: bool) -> u32 {
let _ = bypass;
REFRESH_CALL_COUNT.fetch_add(1, Ordering::SeqCst);
id * 100
}
fn demo_force_refresh() {
println!("\n--- 3. force_refresh = {{ expr }} ---");
REFRESH_CALL_COUNT.store(0, Ordering::SeqCst);
let v1 = get_value(7, false);
assert_eq!(v1, 700);
let runs_after_miss = REFRESH_CALL_COUNT.load(Ordering::SeqCst);
assert_eq!(runs_after_miss, 1, "initial miss must run the body once");
println!(" get_value(7, bypass=false) = {v1} [body ran: {runs_after_miss} time(s) total]");
let v2 = get_value(7, false);
assert_eq!(v2, 700);
let runs_after_hit = REFRESH_CALL_COUNT.load(Ordering::SeqCst);
assert_eq!(
runs_after_hit, 1,
"cache hit must not increment the body counter"
);
println!(
" get_value(7, bypass=false) = {v2} [body ran: {runs_after_hit} time(s) total - cache hit]"
);
let v3 = get_value(7, true);
assert_eq!(v3, 700);
let runs_after_refresh = REFRESH_CALL_COUNT.load(Ordering::SeqCst);
assert_eq!(
runs_after_refresh, 2,
"force_refresh must run the body once more"
);
println!(
" get_value(7, bypass=true) = {v3} [body ran: {runs_after_refresh} time(s) total - forced recompute]"
);
let v4 = get_value(7, false);
assert_eq!(v4, 700);
let runs_final = REFRESH_CALL_COUNT.load(Ordering::SeqCst);
assert_eq!(
runs_final, 2,
"subsequent false-expression call must be a cache hit"
);
println!(
" get_value(7, bypass=false) = {v4} [body ran: {runs_final} time(s) total - cache hit]"
);
println!(" PASS: force_refresh confirmed");
}
fn main() {
demo_sync_writes_by_key();
demo_result_fallback();
demo_force_refresh();
println!("\ndone!");
}