use crate::cache::lru::LruTtlCache; use crate::cache::redis::AppRedis; use crate::config::AppConfig; use crate::error::AppResult; use ::redis::Cmd; use serde::Serialize; use serde::de::DeserializeOwned; use std::time::Duration; pub mod lru; pub mod redis; pub struct AppCache { l1: LruTtlCache, l2: AppRedis, key_prefix: String, default_ttl: Duration, } impl AppCache { pub fn from_config(config: &AppConfig) -> AppResult { let cap = config.lru_default_capacity()?; let ttl = Duration::from_secs(config.lru_default_ttl_secs()?); let l2 = AppRedis::from_config(config)?; let key_prefix = config.redis_key_prefix()?; Ok(Self { l1: LruTtlCache::new(cap, ttl), l2, key_prefix, default_ttl: ttl, }) } pub fn get(&self, key: &str) -> Option { if let Some(json) = self.l1.get(&key.to_string()) { return serde_json::from_str(&json).ok(); } let full_key = self.full_key(key); let mut conn = self.l2.get_connection().ok()?; let json: String = Cmd::new() .arg("GET") .arg(&full_key) .query::>(&mut *conn.inner_mut()) .ok()??; let value: T = serde_json::from_str(&json).ok()?; self.l1.insert(key.to_string(), json); Some(value) } pub fn set(&self, key: &str, value: &T, ttl: Option) -> AppResult<()> { let json = serde_json::to_string(value)?; let full_key = self.full_key(key); let ttl_duration = ttl.unwrap_or(self.default_ttl); let ttl_secs = ttl_duration.as_secs() as usize; let mut conn = self.l2.get_connection()?; Cmd::new() .arg("SETEX") .arg(&full_key) .arg(ttl_secs) .arg(&json) .query::<()>(&mut *conn.inner_mut())?; self.l1.insert_with_ttl(key.to_string(), json, ttl_duration); Ok(()) } pub fn delete(&self, key: &str) -> AppResult<()> { self.l1.remove(&key.to_string()); let full_key = self.full_key(key); let mut conn = self.l2.get_connection()?; Cmd::new() .arg("DEL") .arg(&full_key) .query::<()>(&mut *conn.inner_mut())?; Ok(()) } pub fn exists(&self, key: &str) -> bool { if self.l1.get(&key.to_string()).is_some() { return true; } let full_key = self.full_key(key); if let Ok(mut conn) = self.l2.get_connection() { return Cmd::new() .arg("EXISTS") .arg(&full_key) .query(&mut *conn.inner_mut()) .unwrap_or(false); } false } fn full_key(&self, key: &str) -> String { format!("{}{}", self.key_prefix, key) } }