#![allow(non_snake_case)]
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use std::collections::HashMap as FxHashMap;
use tokio::sync::{RwLock, broadcast};
use crate::cache::core::{RiCache, RiCacheStats};
#[cfg(feature = "pyo3")]
use pyo3::prelude::*;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
pub enum RiCacheEvent {
Invalidate { key: String },
InvalidatePattern { pattern: String },
Clear(),
}
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
pub struct RiCacheManager {
backend: Arc<dyn RiCache + Send + Sync>,
event_sender: broadcast::Sender<RiCacheEvent>,
event_receiver: Option<broadcast::Receiver<RiCacheEvent>>,
_subscribers: Arc<RwLock<FxHashMap<String, broadcast::Receiver<RiCacheEvent>>>>,
}
impl RiCacheManager {
pub fn new(backend: Arc<dyn RiCache + Send + Sync>) -> Self {
let (sender, receiver) = broadcast::channel(100);
Self {
backend,
event_sender: sender,
event_receiver: Some(receiver),
_subscribers: Arc::new(RwLock::new(FxHashMap::default())),
}
}
pub async fn start_consistency_listener(&mut self) -> tokio::task::JoinHandle<()> {
let backend = self.backend.clone();
let mut receiver = match self.event_receiver.take() {
Some(r) => r,
None => {
log::error!("[Ri.Cache] Event receiver already started or not initialized");
return tokio::spawn(async {});
}
};
log::info!("[Ri.Cache] Starting cache consistency event listener");
tokio::spawn(async move {
let mut event_count = 0;
while let Ok(event) = receiver.recv().await {
event_count += 1;
match event {
RiCacheEvent::Invalidate { key } => {
log::info!("[Ri.Cache] Processing invalidate event for key: {key}");
if let Err(e) = backend.delete(&key).await {
log::error!("[Ri.Cache] Failed to invalidate cache key {key}: {e}");
} else {
log::info!("[Ri.Cache] Successfully invalidated cache key: {key}");
}
},
RiCacheEvent::InvalidatePattern { pattern } => {
log::info!("[Ri.Cache] Processing invalidate pattern event: {pattern}");
match backend.delete_by_pattern(&pattern).await {
Ok(count) => {
log::info!("[Ri.Cache] Successfully invalidated {} cache keys matching pattern: {pattern}", count);
}
Err(e) => {
log::error!("[Ri.Cache] Failed to invalidate cache pattern {pattern}: {e}");
}
}
},
RiCacheEvent::Clear() => {
log::info!("[Ri.Cache] Processing clear cache event");
if let Err(e) = backend.clear().await {
log::error!("[Ri.Cache] Failed to clear cache: {e}");
} else {
log::info!("[Ri.Cache] Successfully cleared cache");
}
},
}
if event_count % 100 == 0 {
log::info!("[Ri.Cache] Processed {event_count} cache consistency events");
}
}
log::info!("[Ri.Cache] Cache consistency event listener stopped after processing {event_count} events");
})
}
pub fn subscribe(&self) -> broadcast::Receiver<RiCacheEvent> {
self.event_sender.subscribe()
}
pub fn publish_event(&self, event: RiCacheEvent) {
let event_type = match &event {
RiCacheEvent::Invalidate { key } => format!("Invalidate(key: {key})"),
RiCacheEvent::InvalidatePattern { pattern } => format!("InvalidatePattern(pattern: {pattern})"),
RiCacheEvent::Clear() => "Clear".to_string(),
};
log::info!("[Ri.Cache] Publishing cache event: {event_type}");
let _ = self.event_sender.send(event);
}
pub async fn get<T: serde::de::DeserializeOwned>(&self, key: &str) -> crate::core::RiResult<Option<T>> {
log::debug!("[Ri.Cache] Getting cache key: {key}");
match self.backend.get(key).await? {
Some(cached_value) => {
log::debug!("[Ri.Cache] Cache hit for key: {key}");
let value = serde_json::from_str(&cached_value)
.map_err(|e| crate::core::RiError::Other(format!("Deserialization error: {e}")))?;
Ok(Some(value))
}
None => {
log::debug!("[Ri.Cache] Cache miss for key: {key}");
Ok(None)
}
}
}
pub async fn set<T: serde::Serialize>(&self, key: &str, value: &T, ttl_seconds: Option<u64>) -> crate::core::RiResult<()> {
log::debug!("[Ri.Cache] Setting cache key: {key} with TTL: {ttl_seconds:?}");
let serialized = serde_json::to_string(value)
.map_err(|e| crate::core::RiError::Other(format!("Serialization error: {e}")))?;
let result = self.backend.set(key, &serialized, ttl_seconds).await;
match &result {
Ok(_) => log::debug!("[Ri.Cache] Successfully set cache key: {key}"),
Err(e) => log::error!("[Ri.Cache] Failed to set cache key {key}: {e}"),
}
result
}
pub async fn delete(&self, key: &str) -> crate::core::RiResult<bool> {
log::debug!("[Ri.Cache] Deleting cache key: {key}");
let result = self.backend.delete(key).await;
match &result {
Ok(true) => log::debug!("[Ri.Cache] Successfully deleted cache key: {key}"),
Ok(false) => log::debug!("[Ri.Cache] Cache key not found for deletion: {key}"),
Err(e) => log::error!("[Ri.Cache] Failed to delete cache key {key}: {e}"),
}
self.publish_event(RiCacheEvent::Invalidate { key: key.to_string() });
result
}
pub async fn exists(&self, key: &str) -> bool {
self.backend.exists(key).await
}
pub async fn clear(&self) -> crate::core::RiResult<()> {
log::info!("[Ri.Cache] Clearing all cache entries");
let result = self.backend.clear().await;
match &result {
Ok(_) => log::info!("[Ri.Cache] Successfully cleared all cache entries"),
Err(e) => log::error!("[Ri.Cache] Failed to clear cache: {e}"),
}
self.publish_event(RiCacheEvent::Clear());
result
}
pub async fn invalidate_pattern(&self, pattern: &str) -> crate::core::RiResult<()> {
self.publish_event(RiCacheEvent::InvalidatePattern { pattern: pattern.to_string() });
Ok(())
}
pub async fn stats(&self) -> RiCacheStats {
let stats = self.backend.stats().await;
log::info!("[Ri.Cache] Cache Statistics: hits={}, misses={}, entries={}, hit_rate={:.2}%",
stats.hits, stats.misses, stats.entries, stats.avg_hit_rate * 100.0);
if stats.hits + stats.misses > 0 {
let current_hit_rate = stats.hits as f64 / (stats.hits + stats.misses) as f64;
if current_hit_rate < 0.5 && stats.hits + stats.misses > 100 {
log::warn!("[Ri.Cache] Warning: Low cache hit rate ({:.2}%) with {} total operations",
current_hit_rate * 100.0, stats.hits + stats.misses);
}
}
stats
}
pub async fn cleanup_expired(&self) -> crate::core::RiResult<usize> {
let cleaned = self.backend.cleanup_expired().await?;
if cleaned > 0 {
log::info!("[Ri.Cache] Cleanup completed: {cleaned} expired entries removed");
}
Ok(cleaned)
}
pub async fn get_or_set<T, F>(&self, key: &str, ttl_seconds: Option<u64>, factory: F) -> crate::core::RiResult<T>
where
T: serde::Serialize + serde::de::DeserializeOwned + Clone,
F: FnOnce() -> crate::core::RiResult<T>,
{
log::debug!("[Ri.Cache] get_or_set operation for key: {key} with TTL: {ttl_seconds:?}");
if let Some(value) = self.get::<T>(key).await? {
log::debug!("[Ri.Cache] get_or_set cache hit for key: {key}");
return Ok(value);
}
log::debug!("[Ri.Cache] get_or_set cache miss for key: {key}, generating value");
let value = factory()?;
self.set(key, &value, ttl_seconds).await?;
log::debug!("[Ri.Cache] get_or_set successfully generated and cached value for key: {key}");
Ok(value)
}
}
#[cfg(feature = "pyo3")]
#[pymethods]
impl RiCacheManager {
#[new]
fn py_new() -> Self {
use crate::cache::backends::RiMemoryCache;
let backend = std::sync::Arc::new(RiMemoryCache::new());
Self::new(backend)
}
#[pyo3(name = "get")]
fn get_impl(&self, key: String) -> pyo3::PyResult<Option<String>> {
let rt = tokio::runtime::Runtime::new().map_err(|e| {
pyo3::exceptions::PyRuntimeError::new_err(format!("Failed to create runtime: {}", e))
})?;
rt.block_on(async {
self.get::<String>(&key).await.map_err(|e| {
pyo3::exceptions::PyRuntimeError::new_err(format!("Cache error: {}", e))
})
})
}
#[pyo3(name = "set")]
fn set_impl(&self, key: String, value: String, ttl_seconds: Option<u64>) -> pyo3::PyResult<()> {
let rt = tokio::runtime::Handle::current();
rt.block_on(async {
self.set(&key, &value, ttl_seconds).await.map_err(|e| {
pyo3::exceptions::PyRuntimeError::new_err(format!("Cache error: {}", e))
})
})
}
#[pyo3(name = "delete")]
fn delete_impl(&self, key: String) -> pyo3::PyResult<bool> {
let rt = tokio::runtime::Runtime::new().map_err(|e| {
pyo3::exceptions::PyRuntimeError::new_err(format!("Failed to create runtime: {}", e))
})?;
rt.block_on(async {
self.delete(&key).await.map_err(|e| {
pyo3::exceptions::PyRuntimeError::new_err(format!("Cache error: {}", e))
})
})
}
#[pyo3(name = "exists")]
fn exists_impl(&self, key: String) -> pyo3::PyResult<bool> {
let rt = tokio::runtime::Runtime::new().map_err(|e| {
pyo3::exceptions::PyRuntimeError::new_err(format!("Failed to create runtime: {}", e))
})?;
Ok(rt.block_on(async {
self.exists(&key).await
}))
}
#[pyo3(name = "clear")]
fn clear_impl(&self) -> pyo3::PyResult<()> {
let rt = tokio::runtime::Runtime::new().map_err(|e| {
pyo3::exceptions::PyRuntimeError::new_err(format!("Failed to create runtime: {}", e))
})?;
rt.block_on(async {
self.clear().await.map_err(|e| {
pyo3::exceptions::PyRuntimeError::new_err(format!("Cache error: {}", e))
})
})
}
#[pyo3(name = "stats")]
fn stats_impl(&self) -> pyo3::PyResult<RiCacheStats> {
let rt = tokio::runtime::Runtime::new().map_err(|e| {
pyo3::exceptions::PyRuntimeError::new_err(format!("Failed to create runtime: {}", e))
})?;
Ok(rt.block_on(async {
self.stats().await
}))
}
#[pyo3(name = "cleanup_expired")]
fn cleanup_expired_impl(&self) -> pyo3::PyResult<usize> {
let rt = tokio::runtime::Runtime::new().map_err(|e| {
pyo3::exceptions::PyRuntimeError::new_err(format!("Failed to create runtime: {}", e))
})?;
rt.block_on(async {
self.cleanup_expired().await.map_err(|e| {
pyo3::exceptions::PyRuntimeError::new_err(format!("Cache error: {}", e))
})
})
}
}