use std::fmt;
use std::sync::Arc;
use std::time::Duration;
use cachet::{Cache, CacheEntry, CacheTier, Error, InsertPolicy};
use parking_lot::Mutex;
use tick::Clock;
#[derive(Clone, Debug, PartialEq)]
enum UserData {
Found(String),
NotFound,
}
impl fmt::Display for UserData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Found(name) => write!(f, "Found({name})"),
Self::NotFound => write!(f, "NotFound"),
}
}
}
#[derive(Debug)]
struct Database(Mutex<u32>);
impl CacheTier<String, UserData> for Arc<Database> {
async fn get(&self, key: &String) -> Result<Option<CacheEntry<UserData>>, Error> {
*self.0.lock() += 1;
let data = match key.as_str() {
"user:1" => UserData::Found("Alice".to_string()),
_ => UserData::NotFound,
};
Ok(Some(CacheEntry::new(data)))
}
async fn insert(&self, _: String, _: CacheEntry<UserData>) -> 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 = Arc::new(Database(Mutex::new(0)));
let l2 = Cache::builder::<String, UserData>(clock.clone()).storage(Arc::clone(&db));
let cache = Cache::builder::<String, UserData>(clock)
.memory()
.ttl(Duration::from_secs(60))
.insert_policy(InsertPolicy::when(|e: &CacheEntry<UserData>| {
matches!(e.value(), UserData::NotFound)
}))
.fallback(l2)
.build();
let v = cache.get(&"user:1".to_string()).await.expect("get failed");
match v {
Some(e) => println!("user:1: {}", e.value()),
None => println!("user:1: not found"),
}
let v = cache.get(&"user:2".to_string()).await.expect("get failed");
match v {
Some(e) => println!("user:2: {}", e.value()),
None => println!("user:2: not found"),
}
println!("db calls after first round: {}", *db.0.lock());
cache.get(&"user:1".to_string()).await.expect("get failed"); cache.get(&"user:2".to_string()).await.expect("get failed");
println!("db calls after second round: {}", *db.0.lock());
}