use crate::data::cache::{CacheError, CacheResult, CacheService, CacheStats, CacheType};
use async_trait::async_trait;
use serde::{Serialize, de::DeserializeOwned};
use std::future::Future;
use std::sync::Arc;
use std::time::Duration;
pub async fn cache_aside<T, F, Fut>(
cache: &dyn CacheService,
key: &str,
source_fn: F,
ttl: Option<Duration>,
) -> CacheResult<T>
where
T: DeserializeOwned + Serialize + Send + Sync,
F: FnOnce() -> Fut,
Fut: Future<Output = Result<T, Box<dyn std::error::Error + Send + Sync>>>,
{
if let Some(data) = cache.get(key).await? {
if std::any::type_name::<T>() == std::any::type_name::<String>() {
if let Ok(s) = String::from_utf8(data.clone()) {
let value: T =
serde_json::from_value(serde_json::Value::String(s)).map_err(|e| {
CacheError::Serialization(format!("Failed to deserialize string: {}", e))
})?;
return Ok(value);
}
}
match serde_json::from_slice(&data) {
Ok(value) => return Ok(value),
Err(e) => {
tracing::warn!(key = %key, error = %e, "Failed to deserialize cached data");
}
}
}
let value = source_fn()
.await
.map_err(|e| CacheError::Operation(format!("Failed to fetch data from source: {}", e)))?;
if std::any::type_name::<T>() == std::any::type_name::<String>() {
let string_value: &String = unsafe { std::mem::transmute(&value) };
if let Err(e) = cache.set(key, string_value.as_bytes(), ttl).await {
tracing::error!(key = %key, error = %e, "Failed to set data in cache");
}
} else {
let serialized = serde_json::to_vec(&value)
.map_err(|e| CacheError::Serialization(format!("Failed to serialize data: {}", e)))?;
if let Err(e) = cache.set(key, &serialized, ttl).await {
tracing::error!(key = %key, error = %e, "Failed to set data in cache");
}
}
Ok(value)
}
pub async fn write_through<T, F, Fut>(
cache: &dyn CacheService,
key: &str,
value: &T,
store_fn: F,
ttl: Option<Duration>,
) -> CacheResult<()>
where
T: Serialize + Send + Sync,
F: FnOnce() -> Fut,
Fut: Future<Output = Result<(), Box<dyn std::error::Error + Send + Sync>>>,
{
store_fn()
.await
.map_err(|e| CacheError::Operation(format!("Failed to store data in source: {}", e)))?;
if std::any::type_name::<T>() == std::any::type_name::<String>() {
let string_value: &String = unsafe { std::mem::transmute(value) };
cache.set(key, string_value.as_bytes(), ttl).await?;
} else {
let serialized = serde_json::to_vec(value)
.map_err(|e| CacheError::Serialization(format!("Failed to serialize data: {}", e)))?;
cache.set(key, &serialized, ttl).await?;
}
Ok(())
}
pub async fn write_behind<T, F, Fut>(
cache: &dyn CacheService,
cache_key: &str,
value: &T,
backend_write: F,
ttl: Option<Duration>,
) -> CacheResult<()>
where
T: serde::Serialize + Send + Sync,
F: FnOnce() -> Fut + Send + 'static,
Fut: Future<Output = Result<(), Box<dyn std::error::Error + Send + Sync>>> + Send + 'static,
{
let serialized = serde_json::to_vec(value)
.map_err(|e| CacheError::Serialization(format!("Failed to serialize data: {}", e)))?;
cache.set(cache_key, &serialized, ttl).await?;
let cache_key = cache_key.to_string();
tokio::spawn(async move {
if let Err(e) = backend_write().await {
tracing::error!(key = %cache_key, error = %e, "Failed to save data to backend");
}
});
Ok(())
}
#[async_trait]
pub trait InvalidationStrategy: Send + Sync {
fn generate_key(&self, entity_type: &str, entity_id: &str) -> String;
async fn invalidate(
&self,
cache: &dyn CacheService,
entity_type: &str,
entity_id: &str,
) -> CacheResult<()>;
async fn invalidate_type(&self, cache: &dyn CacheService, entity_type: &str)
-> CacheResult<()>;
}
#[derive(Debug, Clone)]
pub struct TtlInvalidationStrategy {
pub prefix: String,
pub default_ttl: Duration,
}
impl TtlInvalidationStrategy {
pub fn new(prefix: &str, default_ttl: Duration) -> Self {
Self {
prefix: prefix.to_string(),
default_ttl,
}
}
}
#[async_trait]
impl InvalidationStrategy for TtlInvalidationStrategy {
fn generate_key(&self, entity_type: &str, entity_id: &str) -> String {
format!("{}:{}:{}", self.prefix, entity_type, entity_id)
}
async fn invalidate(
&self,
cache: &dyn CacheService,
entity_type: &str,
entity_id: &str,
) -> CacheResult<()> {
let key = self.generate_key(entity_type, entity_id);
let _ = cache.delete(&key).await?;
Ok(())
}
async fn invalidate_type(
&self,
cache: &dyn CacheService,
entity_type: &str,
) -> CacheResult<()> {
let pattern = format!("{}:{}:*", self.prefix, entity_type);
cache.clear(Some(&pattern)).await
}
}
#[derive(Debug, Clone)]
pub struct EventInvalidationStrategy {
pub prefix: String,
pub default_ttl: Duration,
pub use_versioning: bool,
}
impl EventInvalidationStrategy {
pub fn new(prefix: &str, default_ttl: Duration, use_versioning: bool) -> Self {
Self {
prefix: prefix.to_string(),
default_ttl,
use_versioning,
}
}
pub async fn versioned_key(
&self,
cache: &dyn CacheService,
entity_type: &str,
) -> CacheResult<String> {
if !self.use_versioning {
return Ok(self.prefix.clone());
}
let version_key = format!("{}:{}_version", self.prefix, entity_type);
let version = cache.increment(&version_key, 1).await?;
Ok(format!("{}:v{}", self.prefix, version))
}
pub async fn generate_key_async(
&self,
cache: &dyn CacheService,
entity_type: &str,
entity_id: &str,
) -> CacheResult<String> {
if self.use_versioning {
let version_key = format!("{}:{}_version", self.prefix, entity_type);
let current_version = cache.get(&version_key).await?
.and_then(|bytes| String::from_utf8(bytes).ok())
.and_then(|s| s.parse::<i64>().ok())
.unwrap_or(0);
Ok(format!("{}:v{}:{}:{}", self.prefix, current_version, entity_type, entity_id))
} else {
Ok(format!("{}:{}:{}", self.prefix, entity_type, entity_id))
}
}
}
#[async_trait]
impl InvalidationStrategy for EventInvalidationStrategy {
fn generate_key(&self, entity_type: &str, entity_id: &str) -> String {
if self.use_versioning {
format!("{}:{}:{}", self.prefix, entity_type, entity_id)
} else {
format!("{}:{}:{}", self.prefix, entity_type, entity_id)
}
}
async fn invalidate(
&self,
cache: &dyn CacheService,
entity_type: &str,
entity_id: &str,
) -> CacheResult<()> {
if self.use_versioning {
let version_key = format!("{}:{}_version", self.prefix, entity_type);
let _ = cache.increment(&version_key, 1).await?;
} else {
let key = self.generate_key(entity_type, entity_id);
let _ = cache.delete(&key).await?;
}
Ok(())
}
async fn invalidate_type(
&self,
cache: &dyn CacheService,
entity_type: &str,
) -> CacheResult<()> {
if self.use_versioning {
let version_key = format!("{}:{}_version", self.prefix, entity_type);
let _ = cache.increment(&version_key, 1).await?;
Ok(())
} else {
let pattern = format!("{}:{}:*", self.prefix, entity_type);
cache.clear(Some(&pattern)).await
}
}
}
#[derive(Debug, Clone)]
pub struct LruInvalidationStrategy {
pub prefix: String,
pub max_entries: usize,
}
impl LruInvalidationStrategy {
pub fn new(prefix: &str, max_entries: usize) -> Self {
Self {
prefix: prefix.to_string(),
max_entries,
}
}
}
#[async_trait]
impl InvalidationStrategy for LruInvalidationStrategy {
fn generate_key(&self, entity_type: &str, entity_id: &str) -> String {
format!("{}:{}:{}", self.prefix, entity_type, entity_id)
}
async fn invalidate(
&self,
cache: &dyn CacheService,
entity_type: &str,
entity_id: &str,
) -> CacheResult<()> {
let key = self.generate_key(entity_type, entity_id);
let _ = cache.delete(&key).await?;
Ok(())
}
async fn invalidate_type(
&self,
cache: &dyn CacheService,
entity_type: &str,
) -> CacheResult<()> {
let pattern = format!("{}:{}:*", self.prefix, entity_type);
cache.clear(Some(&pattern)).await
}
}
pub struct DistributedLock {
cache: Arc<dyn CacheService>,
key: String,
token: Option<String>,
ttl: Duration,
retry_config: RetryConfig,
}
#[derive(Debug, Clone)]
pub struct RetryConfig {
pub max_attempts: u32,
pub retry_delay: Duration,
pub use_backoff: bool,
}
impl Default for RetryConfig {
fn default() -> Self {
Self {
max_attempts: 5,
retry_delay: Duration::from_millis(100),
use_backoff: true,
}
}
}
impl DistributedLock {
pub fn new(
cache: Arc<Box<dyn CacheService>>,
key: &str,
ttl: Duration,
retry_config: Option<RetryConfig>,
) -> Self {
let cache_service = Arc::new(CacheServiceWrapper(cache));
Self {
cache: cache_service,
key: format!("lock:{}", key),
token: None,
ttl,
retry_config: retry_config.unwrap_or_default(),
}
}
pub async fn try_acquire(&mut self) -> CacheResult<bool> {
let _token = uuid::Uuid::new_v4().to_string();
if let Some(token_str) = self.cache.lock(&self.key, self.ttl).await? {
self.token = Some(token_str);
Ok(true)
} else {
Ok(false)
}
}
pub async fn acquire(&mut self) -> CacheResult<()> {
let mut attempts = 0;
let mut delay = self.retry_config.retry_delay;
while attempts < self.retry_config.max_attempts {
if self.try_acquire().await? {
return Ok(());
}
attempts += 1;
if attempts >= self.retry_config.max_attempts {
break;
}
tokio::time::sleep(delay).await;
if self.retry_config.use_backoff {
delay *= 2;
}
}
Err(CacheError::Internal(format!(
"Failed to acquire lock '{}' after {} attempts",
self.key, self.retry_config.max_attempts
)))
}
pub async fn release(&mut self) -> CacheResult<bool> {
if let Some(token) = &self.token {
let result = self.cache.unlock(&self.key, token).await?;
self.token = None;
Ok(result)
} else {
Ok(false)
}
}
pub fn is_acquired(&self) -> bool {
self.token.is_some()
}
pub async fn with_lock<F, Fut, T, E>(&mut self, f: F) -> Result<T, LockError<E>>
where
F: FnOnce() -> Fut,
Fut: Future<Output = Result<T, E>>,
E: std::error::Error + Send + Sync + 'static,
{
self.acquire().await.map_err(LockError::Acquisition)?;
let result = f().await;
let _ = self.release().await;
result.map_err(LockError::Operation)
}
}
#[derive(Debug)]
pub enum LockError<E> {
Acquisition(CacheError),
Operation(E),
Release(CacheError),
}
impl<E: std::fmt::Display> std::fmt::Display for LockError<E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Acquisition(e) => write!(f, "Failed to acquire lock: {}", e),
Self::Operation(e) => write!(f, "Operation error: {}", e),
Self::Release(e) => write!(f, "Failed to release lock: {}", e),
}
}
}
impl<E: std::error::Error + 'static> std::error::Error for LockError<E> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Acquisition(e) => Some(e),
Self::Operation(e) => Some(e),
Self::Release(e) => Some(e),
}
}
}
impl<E: std::fmt::Display> From<LockError<E>> for CacheError {
fn from(error: LockError<E>) -> Self {
match error {
LockError::Acquisition(e) => e,
LockError::Operation(e) => CacheError::Operation(format!("Operation error: {}", e)),
LockError::Release(e) => e,
}
}
}
struct CacheServiceWrapper(Arc<Box<dyn CacheService>>);
#[async_trait]
impl CacheService for CacheServiceWrapper {
async fn get(&self, key: &str) -> CacheResult<Option<Vec<u8>>> {
self.0.get(key).await
}
async fn set(&self, key: &str, value: &[u8], ttl: Option<Duration>) -> CacheResult<()> {
self.0.set(key, value, ttl).await
}
async fn delete(&self, key: &str) -> CacheResult<bool> {
self.0.delete(key).await
}
async fn flush(&self) -> CacheResult<()> {
self.0.flush().await
}
async fn stats(&self) -> CacheResult<CacheStats> {
self.0.stats().await
}
async fn ping(&self) -> CacheResult<()> {
self.0.ping().await
}
async fn close(&self) -> CacheResult<()> {
self.0.close().await
}
async fn exists(&self, key: &str) -> CacheResult<bool> {
self.0.exists(key).await
}
async fn set_nx(&self, key: &str, value: &[u8], ttl: Option<Duration>) -> CacheResult<bool> {
self.0.set_nx(key, value, ttl).await
}
async fn get_set(&self, key: &str, value: &[u8]) -> CacheResult<Option<Vec<u8>>> {
self.0.get_set(key, value).await
}
async fn increment(&self, key: &str, delta: i64) -> CacheResult<i64> {
self.0.increment(key, delta).await
}
async fn decrement(&self, key: &str, delta: i64) -> CacheResult<i64> {
self.0.decrement(key, delta).await
}
async fn set_many(
&self,
items: &std::collections::HashMap<String, Vec<u8>>,
ttl: Option<Duration>,
) -> CacheResult<()> {
self.0.set_many(items, ttl).await
}
async fn get_many(
&self,
keys: &[String],
) -> CacheResult<std::collections::HashMap<String, Vec<u8>>> {
self.0.get_many(keys).await
}
async fn delete_many(&self, keys: &[String]) -> CacheResult<u64> {
self.0.delete_many(keys).await
}
async fn clear(&self, namespace: Option<&str>) -> CacheResult<()> {
self.0.clear(namespace).await
}
async fn lock(&self, key: &str, ttl: Duration) -> CacheResult<Option<String>> {
self.0.lock(key, ttl).await
}
async fn unlock(&self, key: &str, token: &str) -> CacheResult<bool> {
self.0.unlock(key, token).await
}
fn get_cache_type(&self) -> CacheType {
self.0.get_cache_type()
}
fn get_default_ttl(&self) -> Duration {
self.0.get_default_ttl()
}
}