88 lines
2.9 KiB
Python
88 lines
2.9 KiB
Python
import redis.asyncio as redis
|
|
import json
|
|
import logging
|
|
from typing import Any, Optional
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class CacheService:
|
|
"""Redis cache service with JSON serialization"""
|
|
|
|
def __init__(self, redis_client: Optional[redis.Redis], enabled: bool = True, default_ttl: int = 3600):
|
|
self.redis = redis_client
|
|
self.enabled = enabled and redis_client is not None
|
|
self.default_ttl = default_ttl
|
|
|
|
async def get(self, key: str) -> Optional[Any]:
|
|
"""Get value from cache"""
|
|
if not self.enabled:
|
|
return None
|
|
|
|
try:
|
|
value = await self.redis.get(key)
|
|
if value:
|
|
return json.loads(value)
|
|
return None
|
|
except Exception as e:
|
|
logger.error(f"Cache get error for key {key}: {e}")
|
|
return None
|
|
|
|
async def set(self, key: str, value: Any, ttl: Optional[int] = None) -> bool:
|
|
"""Set value in cache"""
|
|
if not self.enabled:
|
|
return False
|
|
|
|
try:
|
|
ttl = ttl or self.default_ttl
|
|
json_value = json.dumps(value, default=str) # Handle datetime objects
|
|
await self.redis.setex(key, ttl, json_value)
|
|
return True
|
|
except Exception as e:
|
|
logger.error(f"Cache set error for key {key}: {e}")
|
|
return False
|
|
|
|
async def delete(self, key: str) -> bool:
|
|
"""Delete key from cache"""
|
|
if not self.enabled:
|
|
return False
|
|
|
|
try:
|
|
deleted = await self.redis.delete(key)
|
|
return deleted > 0
|
|
except Exception as e:
|
|
logger.error(f"Cache delete error for key {key}: {e}")
|
|
return False
|
|
|
|
async def invalidate_dropdown_cache(self) -> int:
|
|
"""Invalidate all dropdown cache entries"""
|
|
if not self.enabled:
|
|
return 0
|
|
|
|
try:
|
|
pattern = "dropdown:*"
|
|
keys = await self.redis.keys(pattern)
|
|
if keys:
|
|
deleted = await self.redis.delete(*keys)
|
|
logger.info(f"Invalidated {deleted} dropdown cache entries")
|
|
return deleted
|
|
return 0
|
|
except Exception as e:
|
|
logger.error(f"Cache invalidation error: {e}")
|
|
return 0
|
|
|
|
async def get_stats(self) -> dict:
|
|
"""Get cache statistics"""
|
|
if not self.enabled:
|
|
return {"enabled": False}
|
|
|
|
try:
|
|
info = await self.redis.info("memory")
|
|
return {
|
|
"enabled": True,
|
|
"used_memory": info.get("used_memory_human"),
|
|
"used_memory_peak": info.get("used_memory_peak_human"),
|
|
"connected_clients": await self.redis.client_list()
|
|
}
|
|
except Exception as e:
|
|
logger.error(f"Cache stats error: {e}")
|
|
return {"enabled": True, "error": str(e)} |