1000f8a80d
- Add gRPC service modules: auth, channel, channel settings, member, permission - Update protobuf definitions and generated code - Remove immediate/ real-time module (superseded by IM service) - Update etcd discovery and registration - Update cache, error, config, and build infrastructure - Add ADR documentation - Update OpenAPI spec
139 lines
3.9 KiB
Rust
139 lines
3.9 KiB
Rust
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<String, String>,
|
|
l2: AppRedis,
|
|
key_prefix: String,
|
|
default_ttl: Duration,
|
|
}
|
|
|
|
impl AppCache {
|
|
pub async fn from_config(config: &AppConfig) -> AppResult<Self> {
|
|
let cap = config.lru_default_capacity()?;
|
|
let ttl = Duration::from_secs(config.lru_default_ttl_secs()?);
|
|
let l2 = AppRedis::from_config(config).await?;
|
|
let key_prefix = config.redis_key_prefix()?;
|
|
Ok(Self {
|
|
l1: LruTtlCache::new(cap, ttl),
|
|
l2,
|
|
key_prefix,
|
|
default_ttl: ttl,
|
|
})
|
|
}
|
|
|
|
pub async fn get<T: DeserializeOwned>(&self, key: &str) -> Option<T> {
|
|
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();
|
|
let json: String = Cmd::new()
|
|
.arg("GET")
|
|
.arg(&full_key)
|
|
.query_async::<Option<String>>(&mut conn)
|
|
.await
|
|
.ok()??;
|
|
|
|
let value: T = serde_json::from_str(&json).ok()?;
|
|
self.l1.insert(key.to_string(), json);
|
|
Some(value)
|
|
}
|
|
|
|
pub async fn get_l2_only<T: DeserializeOwned>(&self, key: &str) -> Option<T> {
|
|
let full_key = self.full_key(key);
|
|
let mut conn = self.l2.get_connection();
|
|
let json: String = Cmd::new()
|
|
.arg("GET")
|
|
.arg(&full_key)
|
|
.query_async::<Option<String>>(&mut conn)
|
|
.await
|
|
.ok()??;
|
|
|
|
serde_json::from_str(&json).ok()
|
|
}
|
|
|
|
pub async fn set<T: Serialize>(
|
|
&self,
|
|
key: &str,
|
|
value: &T,
|
|
ttl: Option<Duration>,
|
|
) -> 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_async::<()>(&mut conn)
|
|
.await?;
|
|
self.l1.insert_with_ttl(key.to_string(), json, ttl_duration);
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn set_l2_only<T: Serialize>(
|
|
&self,
|
|
key: &str,
|
|
value: &T,
|
|
ttl: Option<Duration>,
|
|
) -> 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_async::<()>(&mut conn)
|
|
.await?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async 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_async::<()>(&mut conn)
|
|
.await?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn exists(&self, key: &str) -> bool {
|
|
if self.l1.get(&key.to_string()).is_some() {
|
|
return true;
|
|
}
|
|
let full_key = self.full_key(key);
|
|
let mut conn = self.l2.get_connection();
|
|
Cmd::new()
|
|
.arg("EXISTS")
|
|
.arg(&full_key)
|
|
.query_async::<bool>(&mut conn)
|
|
.await
|
|
.unwrap_or(false)
|
|
}
|
|
|
|
fn full_key(&self, key: &str) -> String {
|
|
format!("{}{}", self.key_prefix, key)
|
|
}
|
|
}
|