use std::time::{Duration, Instant};
use amalgam::{Cache, EntryOptions, Timeout};
#[tokio::main]
async fn main() {
let cache: Cache<String> = Cache::new();
let opts = EntryOptions::new(Duration::from_millis(200))
.with_fail_safe(
true,
Some(Duration::from_secs(60 * 60)),
Some(Duration::from_secs(1)),
)
.with_factory_timeouts(
Timeout::After(Duration::from_millis(50)), Timeout::Infinite, true, );
cache
.get_or_set_with(
"feed",
|ctx| async move { Ok(ctx.value("v1".to_owned())) },
opts.clone(),
)
.await
.expect("priming succeeds");
println!("primed value => \"v1\"");
tokio::time::sleep(Duration::from_millis(250)).await;
println!("(slept 250ms — \"feed\" is now stale)");
let started = Instant::now();
let served = cache
.get_or_set_with(
"feed",
|ctx| async move {
println!(" [factory] started (will take 300ms)…");
tokio::time::sleep(Duration::from_millis(300)).await;
println!(" [factory] finished in the background, producing \"v2\"");
Ok(ctx.value("v2".to_owned()))
},
opts,
)
.await
.expect("soft timeout returns the stale value");
let waited = started.elapsed();
println!(
"served after ~{}ms => {served:?} (stale value returned immediately)",
waited.as_millis()
);
assert_eq!(served, "v1", "soft timeout returns the stale value");
assert!(
waited < Duration::from_millis(250),
"the caller should not have waited for the full 300ms factory"
);
tokio::time::sleep(Duration::from_millis(400)).await;
let now_cached = cache.try_get("feed", None).await;
println!(
"cache now holds => {:?} (updated by the background factory)",
now_cached.value()
);
assert_eq!(
now_cached.value(),
Some(&"v2".to_owned()),
"the background completion should have updated the cache"
);
println!("OK: fast stale response, then background refresh to \"v2\".");
}