diff --git a/.env.example b/.env.example index 10f5bd88..aa73850c 100644 --- a/.env.example +++ b/.env.example @@ -21,6 +21,9 @@ LOG_DIR="/opt/sqlbot/logs" LOG_FORMAT="%(asctime)s - %(name)s - %(levelname)s:%(lineno)d - %(message)s" SQL_DEBUG=False +CACHE_TYPE="memory" +CACHE_REDIS_URL="redis://127.0.0.1:6379" + # Postgres POSTGRES_SERVER=localhost POSTGRES_PORT=5432 diff --git a/backend/common/core/config.py b/backend/common/core/config.py index f9026b09..482f4d61 100644 --- a/backend/common/core/config.py +++ b/backend/common/core/config.py @@ -61,6 +61,9 @@ class Settings(BaseSettings): DEFAULT_PWD: str = "SQLBot@123456" ASSISTANT_TOKEN_KEY: str = "X-SQLBOT-ASSISTANT-TOKEN" + CACHE_TYPE: Literal["redis", "memory", "None"] = "memory" + CACHE_REDIS_URL: str | None = None # Redis URL, e.g., "redis://[[username]:[password]]@localhost:6379/0" + LOG_LEVEL: str = "INFO" # DEBUG, INFO, WARNING, ERROR LOG_DIR: str = "logs" LOG_FORMAT: str = "%(asctime)s - %(name)s - %(levelname)s:%(lineno)d - %(message)s" diff --git a/backend/common/core/sqlbot_cache.py b/backend/common/core/sqlbot_cache.py index 15d58308..02cd2d82 100644 --- a/backend/common/core/sqlbot_cache.py +++ b/backend/common/core/sqlbot_cache.py @@ -7,11 +7,9 @@ from contextlib import asynccontextmanager import asyncio import random from collections import defaultdict - -from apps.system.schemas.auth import CacheName -from apps.system.schemas.system_schema import UserInfoDTO +from common.core.config import settings from common.utils.utils import SQLBotLogUtil - +from fastapi_cache.backends.inmemory import InMemoryBackend # 使用contextvar来跟踪当前线程已持有的锁 _held_locks = contextvars.ContextVar('held_locks', default=set()) # 高效锁管理器 @@ -105,6 +103,8 @@ def cache( @wraps(func) async def wrapper(*args, **kwargs): + if not settings.CACHE_TYPE or settings.CACHE_TYPE.lower() == "none": + return await func(*args, **kwargs) # 生成缓存键 cache_key = used_key_builder( func=func, @@ -116,13 +116,9 @@ def cache( # 防击穿锁 async with _get_cache_lock(cache_key): backend = FastAPICache.get_backend() - # 双重检查 if (cached := await backend.get(cache_key)) is not None: SQLBotLogUtil.debug(f"Cache hit: {cache_key}") - if CacheName.USER_INFO.value in cache_key: - user = UserInfoDTO.model_validate(cached) - SQLBotLogUtil.info(f"User cache hit: [uid: {user.id}, account: {user.account}, oid: {user.oid}]") return cached # 执行函数并缓存结果 @@ -134,9 +130,6 @@ def cache( await backend.set(cache_key, result, actual_expire) SQLBotLogUtil.debug(f"Cache set: {cache_key} (expire: {actual_expire}s)") - if CacheName.USER_INFO.value in cache_key: - user = UserInfoDTO.model_validate(result) - SQLBotLogUtil.info(f"User cache set: [uid: {user.id}, account: {user.account}, oid: {user.oid}]") return result return wrapper @@ -152,6 +145,8 @@ def clear_cache( def decorator(func): @wraps(func) async def wrapper(*args, **kwargs): + if not settings.CACHE_TYPE or settings.CACHE_TYPE.lower() == "none": + return await func(*args, **kwargs) cache_key = custom_key_builder( func=func, namespace=namespace, @@ -170,8 +165,27 @@ def clear_cache( result = await func(*args, **kwargs) if await backend.get(cache_key): await backend.clear(cache_key) - SQLBotLogUtil.info(f"Cache cleared: {cache_key}") + SQLBotLogUtil.debug(f"Cache cleared: {cache_key}") return result return wrapper - return decorator \ No newline at end of file + return decorator + + +def init_sqlbot_cache(): + cache_type: str = settings.CACHE_TYPE + if cache_type == "memory": + FastAPICache.init(InMemoryBackend()) + SQLBotLogUtil.info("SQLBot 使用内存缓存, 仅支持单进程模式") + elif cache_type == "redis": + from fastapi_cache.backends.redis import RedisBackend + import redis.asyncio as redis + from redis.asyncio.connection import ConnectionPool + redis_url = settings.CACHE_REDIS_URL or "redis://localhost:6379/0" + pool = ConnectionPool.from_url(url=redis_url) + redis_client = redis.Redis(connection_pool=pool) + FastAPICache.init(RedisBackend(redis_client), prefix="sqlbot-cache") + SQLBotLogUtil.info(f"SQLBot 使用Redis缓存, 可使用多进程模式") + else: + SQLBotLogUtil.warning("SQLBot 未启用缓存, 可使用多进程模式") + \ No newline at end of file diff --git a/backend/main.py b/backend/main.py index 54410660..c4571893 100644 --- a/backend/main.py +++ b/backend/main.py @@ -14,10 +14,9 @@ from alembic import command from fastapi_mcp import FastApiMCP from fastapi.staticfiles import StaticFiles import sqlbot_xpack -from fastapi_cache import FastAPICache -from fastapi_cache.backends.inmemory import InMemoryBackend from common.utils.utils import SQLBotLogUtil +from common.core.sqlbot_cache import init_sqlbot_cache def run_migrations(): alembic_cfg = Config("alembic.ini") @@ -27,7 +26,7 @@ def run_migrations(): @asynccontextmanager async def lifespan(app: FastAPI): run_migrations() - FastAPICache.init(InMemoryBackend()) + init_sqlbot_cache() init_dynamic_cors(app) SQLBotLogUtil.info("✅ SQLBot 初始化完成") yield diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 093e01ce..1de57be8 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -39,6 +39,7 @@ dependencies = [ "sqlbot-xpack==0.0.3.7", "fastapi-cache2>=0.2.2", "sqlparse>=0.5.3", + "redis>=6.2.0", ] [[tool.uv.index]] name = "default"