Files
DinQuant/backend_api_python/app/utils/cache.py
T
TIANHE f43312a858 creat
Signed-off-by: TIANHE <TIANHE@GMAIL.COM>
2025-12-29 03:06:49 +08:00

129 lines
3.6 KiB
Python

"""
Cache utilities.
Local-first behavior: use in-memory cache by default.
Redis is only used when explicitly enabled via environment variables.
"""
import time
import threading
from typing import Optional, Any
import json
from app.utils.logger import get_logger
from app.config import CacheConfig
logger = get_logger(__name__)
class MemoryCache:
"""内存缓存(Redis 不可用时的备选方案)"""
def __init__(self):
self._cache = {}
self._lock = threading.Lock()
def get(self, key: str) -> Optional[str]:
with self._lock:
if key in self._cache:
data, expiry = self._cache[key]
if expiry > time.time():
return data
else:
del self._cache[key]
return None
def setex(self, key: str, ttl: int, value: str):
with self._lock:
expiry = time.time() + ttl
self._cache[key] = (value, expiry)
def delete(self, key: str):
with self._lock:
if key in self._cache:
del self._cache[key]
def clear(self):
with self._lock:
self._cache.clear()
class CacheManager:
"""缓存管理器"""
_instance = None
_lock = threading.Lock()
def __new__(cls):
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._initialized = False
return cls._instance
def __init__(self):
if self._initialized:
return
self._initialized = True
self._client = None
self._use_redis = False
# Local-first: do NOT touch Redis unless explicitly enabled.
if not CacheConfig.ENABLED:
self._client = MemoryCache()
self._use_redis = False
return
# Try Redis only when enabled.
try:
import redis
from app.config import RedisConfig
self._client = redis.Redis(
host=RedisConfig.HOST,
port=RedisConfig.PORT,
db=RedisConfig.DB,
password=RedisConfig.PASSWORD,
decode_responses=True,
socket_connect_timeout=RedisConfig.CONNECT_TIMEOUT,
socket_timeout=RedisConfig.SOCKET_TIMEOUT
)
self._client.ping()
self._use_redis = True
logger.info("Redis cache connected")
except Exception as e:
# Fall back silently (keep startup logs clean in local mode).
logger.info(f"Redis is enabled but unavailable; using in-memory cache instead: {e}")
self._client = MemoryCache()
self._use_redis = False
def get(self, key: str) -> Optional[Any]:
"""获取缓存"""
try:
data = self._client.get(key)
if data:
return json.loads(data)
return None
except Exception as e:
logger.error(f"Cache read failed: {e}")
return None
def set(self, key: str, value: Any, ttl: int = 300):
"""设置缓存"""
try:
self._client.setex(key, ttl, json.dumps(value))
except Exception as e:
logger.error(f"Cache write failed: {e}")
def delete(self, key: str):
"""删除缓存"""
try:
self._client.delete(key)
except Exception as e:
logger.error(f"Cache delete failed: {e}")
@property
def is_redis(self) -> bool:
return self._use_redis