ai-robot-core/ai-service/app/services/metadata_cache_service.py

203 lines
5.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
元数据字段定义缓存服务
使用 Redis 缓存 metadata_field_definitions减少数据库查询
"""
import json
import logging
from typing import Any
from app.core.config import get_settings
logger = logging.getLogger(__name__)
class MetadataCacheService:
"""
元数据字段定义缓存服务
缓存策略:
- Key: metadata:fields:{tenant_id}
- Value: JSON 序列化的字段定义列表
- TTL: 1小时3600秒
- 更新策略:写时更新 + 定时刷新
"""
CACHE_KEY_PREFIX = "metadata:fields"
DEFAULT_TTL = 3600 # 1小时
def __init__(self):
self._settings = get_settings()
self._redis_client = None
self._enabled = self._settings.redis_enabled
async def _get_redis(self):
"""获取 Redis 连接(延迟初始化)"""
if not self._enabled:
return None
if self._redis_client is None:
try:
import redis.asyncio as redis
self._redis_client = redis.from_url(
self._settings.redis_url,
decode_responses=True
)
except Exception as e:
logger.error(f"[MetadataCache] Failed to connect Redis: {e}")
self._enabled = False
return None
return self._redis_client
def _make_key(self, tenant_id: str) -> str:
"""生成缓存 key"""
return f"{self.CACHE_KEY_PREFIX}:{tenant_id}"
async def get_fields(self, tenant_id: str) -> list[dict[str, Any]] | None:
"""
获取缓存的字段定义
Args:
tenant_id: 租户 ID
Returns:
字段定义列表,未缓存返回 None
"""
if not self._enabled:
return None
try:
redis = await self._get_redis()
if not redis:
return None
key = self._make_key(tenant_id)
cached_data = await redis.get(key)
if cached_data:
logger.info(f"[MetadataCache] Cache hit for tenant={tenant_id}")
return json.loads(cached_data)
logger.info(f"[MetadataCache] Cache miss for tenant={tenant_id}")
return None
except Exception as e:
logger.error(f"[MetadataCache] Get cache error: {e}")
return None
async def set_fields(
self,
tenant_id: str,
fields: list[dict[str, Any]],
ttl: int | None = None
) -> bool:
"""
缓存字段定义
Args:
tenant_id: 租户 ID
fields: 字段定义列表
ttl: 过期时间(秒),默认 1小时
Returns:
是否成功
"""
if not self._enabled:
return False
try:
redis = await self._get_redis()
if not redis:
return False
key = self._make_key(tenant_id)
ttl = ttl or self.DEFAULT_TTL
await redis.setex(
key,
ttl,
json.dumps(fields, ensure_ascii=False, default=str)
)
logger.info(
f"[MetadataCache] Cached {len(fields)} fields for tenant={tenant_id}, "
f"ttl={ttl}s"
)
return True
except Exception as e:
logger.error(f"[MetadataCache] Set cache error: {e}")
return False
async def invalidate(self, tenant_id: str) -> bool:
"""
使缓存失效(字段定义更新时调用)
Args:
tenant_id: 租户 ID
Returns:
是否成功
"""
if not self._enabled:
return False
try:
redis = await self._get_redis()
if not redis:
return False
key = self._make_key(tenant_id)
result = await redis.delete(key)
if result:
logger.info(f"[MetadataCache] Invalidated cache for tenant={tenant_id}")
return bool(result)
except Exception as e:
logger.error(f"[MetadataCache] Invalidate error: {e}")
return False
async def invalidate_all(self) -> bool:
"""
使所有元数据缓存失效
Returns:
是否成功
"""
if not self._enabled:
return False
try:
redis = await self._get_redis()
if not redis:
return False
# 查找所有元数据缓存 key
pattern = f"{self.CACHE_KEY_PREFIX}:*"
keys = []
async for key in redis.scan_iter(match=pattern):
keys.append(key)
if keys:
await redis.delete(*keys)
logger.info(f"[MetadataCache] Invalidated {len(keys)} cache entries")
return True
except Exception as e:
logger.error(f"[MetadataCache] Invalidate all error: {e}")
return False
# 全局缓存服务实例
_metadata_cache_service: MetadataCacheService | None = None
async def get_metadata_cache_service() -> MetadataCacheService:
"""获取元数据缓存服务实例(单例)"""
global _metadata_cache_service
if _metadata_cache_service is None:
_metadata_cache_service = MetadataCacheService()
return _metadata_cache_service