Skip to content
Clone
971c37c
134 lines 3.7 KB Rust
use std::sync::Arc;
use std::time::Duration;
use config::cache::CacheConfig;
use config::redis::RedisMode;
use oxcache::backend::{
CacheReader, CacheWriter, MokaMemoryBackend, RedisBackend, RedisMode as OxRedisMode,
};
use oxcache::cache::{ChainCache, ChainLink};
use serde::Serialize;
use serde::de::DeserializeOwned;
use crate::error::CacheError;
pub struct CacheClient {
chain: Arc<ChainCache>,
}
impl CacheClient {
pub async fn new(config: &CacheConfig) -> Result<Self, CacheError> {
unsafe {
std::env::set_var("OXCACHE_ALLOW_INSECURE_REDIS", "I_UNDERSTAND_THE_RISKS");
}
config.validate().map_err(CacheError::InvalidConfig)?;
let l1 = build_l1(config)?;
let l2 = build_l2(config).await?;
let mut builder = ChainCache::builder()
.link(ChainLink::from_backend(l1))
.link(ChainLink::from_backend(l2));
if config.backfill_enabled {
builder = builder.enable_backfill();
}
if let Some(ttl) = config.default_ttl_secs {
builder = builder.default_time_to_live(Duration::from_secs(ttl));
}
Ok(Self {
chain: Arc::new(builder.build()),
})
}
pub fn raw(&self) -> &ChainCache {
&self.chain
}
pub async fn get<V>(&self, key: &str) -> Result<Option<V>, CacheError>
where
V: DeserializeOwned,
{
let raw = self.chain.get(key).await?;
match raw {
Some(bytes) => {
let value = serde_json::from_slice::<V>(&bytes)?;
Ok(Some(value))
}
None => Ok(None),
}
}
pub async fn set<V>(
&self,
key: &str,
value: &V,
ttl: Option<Duration>,
) -> Result<(), CacheError>
where
V: Serialize,
{
let bytes = serde_json::to_vec(value)?;
self.chain.set(key, bytes, ttl).await?;
Ok(())
}
pub async fn delete(&self, key: &str) -> Result<(), CacheError> {
self.chain.delete(key).await?;
Ok(())
}
pub async fn exists(&self, key: &str) -> Result<bool, CacheError> {
Ok(self.chain.exists(key).await?)
}
pub async fn get_bytes(&self, key: &str) -> Result<Option<Vec<u8>>, CacheError> {
Ok(self.chain.get(key).await?)
}
pub async fn set_bytes(
&self,
key: &str,
value: Vec<u8>,
ttl: Option<Duration>,
) -> Result<(), CacheError> {
self.chain.set(key, value, ttl).await?;
Ok(())
}
}
fn build_l1(config: &CacheConfig) -> Result<MokaMemoryBackend, CacheError> {
let mut builder = MokaMemoryBackend::builder().capacity(config.l1_capacity);
if let Some(ttl) = config.l1_ttl_secs {
builder = builder.ttl(Duration::from_secs(ttl));
}
if let Some(tti) = config.l1_tti_secs {
builder = builder.time_to_idle(Duration::from_secs(tti));
}
Ok(builder.build())
}
async fn build_l2(config: &CacheConfig) -> Result<RedisBackend, CacheError> {
let url = config.redis.validate().map_err(CacheError::InvalidConfig)?;
let mut builder = RedisBackend::builder()
.connection_string(url)
.mode(to_oxcache_mode(config.redis.mode()))
.pool_size(config.redis.pool_size())
.connection_timeout(config.redis.connection_timeout());
if let Some(db) = config.redis.database {
builder = builder.database(db);
}
Ok(builder.build().await?)
}
fn to_oxcache_mode(mode: RedisMode) -> OxRedisMode {
match mode {
RedisMode::Standalone => OxRedisMode::Standalone,
RedisMode::Sentinel => OxRedisMode::Sentinel,
RedisMode::Cluster => OxRedisMode::Cluster,
RedisMode::ValkeyStandalone => OxRedisMode::ValkeyStandalone,
}
}