use std::sync::Arc;
use std::sync::atomic::{AtomicU32, Ordering};
use std::time::Duration;
use anyspawn::Spawner;
use cachet::{Cache, CacheEntry, CacheTier, Error, TimeToRefresh};
use tick::Clock;
#[derive(Clone)]
struct Database {
calls: Arc<AtomicU32>,
clock: Clock,
}
impl CacheTier<String, String> for Database {
async fn get(&self, key: &String) -> Result<Option<CacheEntry<String>>, Error> {
let v = self.calls.fetch_add(1, Ordering::Relaxed) + 1;
self.clock.delay(Duration::from_millis(50)).await;
Ok(Some(CacheEntry::new(format!("{key}_v{v}"))))
}
async fn insert(&self, _: String, _: CacheEntry<String>) -> Result<(), Error> {
Ok(())
}
async fn invalidate(&self, _: &String) -> Result<(), Error> {
Ok(())
}
async fn clear(&self) -> Result<(), Error> {
Ok(())
}
}
#[tokio::main]
async fn main() {
let clock = Clock::new_tokio();
let db = Database {
calls: Arc::new(AtomicU32::new(0)),
clock: clock.clone(),
};
let cache = Cache::builder::<String, String>(clock.clone())
.memory()
.ttl(Duration::from_secs(10))
.fallback(Cache::builder::<String, String>(clock.clone()).storage(db.clone()))
.time_to_refresh(TimeToRefresh::new(Duration::from_secs(1), Spawner::new_tokio()))
.build();
let key = "config".to_string();
let v = cache.get(&key).await.expect("get failed");
match v {
Some(e) => println!("initial: {} (db calls: {})", e.value(), db.calls.load(Ordering::Relaxed)),
None => println!("initial: not found (db calls: {})", db.calls.load(Ordering::Relaxed)),
}
clock.delay(Duration::from_millis(1500)).await;
let v = cache.get(&key).await.expect("get failed");
match v {
Some(e) => println!("stale: {}", e.value()),
None => println!("stale: not found"),
}
clock.delay(Duration::from_millis(100)).await;
let v = cache.get(&key).await.expect("get failed");
match v {
Some(e) => println!("refreshed: {} (db calls: {})", e.value(), db.calls.load(Ordering::Relaxed)),
None => println!("refreshed: not found (db calls: {})", db.calls.load(Ordering::Relaxed)),
}
}