mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
refactor: 移除启动缓存预热功能
删除 cache_warmup.py 及相关配置项和测试,简化启动流程
This commit is contained in:
@@ -313,9 +313,7 @@ class Config:
|
|||||||
self.management_token_max_per_user = int(os.getenv("MANAGEMENT_TOKEN_MAX_PER_USER", "20"))
|
self.management_token_max_per_user = int(os.getenv("MANAGEMENT_TOKEN_MAX_PER_USER", "20"))
|
||||||
|
|
||||||
# 启动任务开关
|
# 启动任务开关
|
||||||
# CACHE_WARMUP_ENABLED: 是否在启动时执行缓存预热
|
|
||||||
# MAINTENANCE_STARTUP_TASKS_ENABLED: 是否在启动时执行维护调度器初始化任务(清理、统计回填等)
|
# MAINTENANCE_STARTUP_TASKS_ENABLED: 是否在启动时执行维护调度器初始化任务(清理、统计回填等)
|
||||||
self.cache_warmup_enabled = os.getenv("CACHE_WARMUP_ENABLED", "true").lower() == "true"
|
|
||||||
self.maintenance_startup_tasks_enabled = (
|
self.maintenance_startup_tasks_enabled = (
|
||||||
os.getenv("MAINTENANCE_STARTUP_TASKS_ENABLED", "true").lower() == "true"
|
os.getenv("MAINTENANCE_STARTUP_TASKS_ENABLED", "true").lower() == "true"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -340,11 +340,6 @@ async def _start_background_services(state: LifecycleState) -> None:
|
|||||||
state.task_scheduler = get_scheduler()
|
state.task_scheduler = get_scheduler()
|
||||||
state.task_scheduler.start()
|
state.task_scheduler.start()
|
||||||
|
|
||||||
# 启动缓存预热(后台任务,不阻塞启动)
|
|
||||||
from src.services.system.cache_warmup import start_cache_warmup
|
|
||||||
|
|
||||||
await start_cache_warmup()
|
|
||||||
|
|
||||||
|
|
||||||
async def _run_startup(app: FastAPI) -> LifecycleState:
|
async def _run_startup(app: FastAPI) -> LifecycleState:
|
||||||
"""执行完整启动流程并返回生命周期状态。"""
|
"""执行完整启动流程并返回生命周期状态。"""
|
||||||
|
|||||||
@@ -1,180 +0,0 @@
|
|||||||
"""
|
|
||||||
缓存预热服务
|
|
||||||
|
|
||||||
在应用启动时预热关键缓存,避免用户首次访问时的长时间等待。
|
|
||||||
|
|
||||||
预热的缓存包括:
|
|
||||||
- 管理员仪表盘统计数据
|
|
||||||
- 管理员热力图数据
|
|
||||||
- 每日统计数据
|
|
||||||
"""
|
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import importlib
|
|
||||||
import time
|
|
||||||
from dataclasses import dataclass, field
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
from sqlalchemy.orm import Session
|
|
||||||
|
|
||||||
from src.config.settings import config
|
|
||||||
from src.core.enums import UserRole
|
|
||||||
from src.core.logger import logger
|
|
||||||
from src.database import create_session
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class WarmupContext:
|
|
||||||
"""缓存预热专用的简化 Context"""
|
|
||||||
|
|
||||||
db: Session
|
|
||||||
user: Any # User model
|
|
||||||
audit_metadata: dict[str, Any] = field(default_factory=dict)
|
|
||||||
|
|
||||||
def add_audit_metadata(self, **kwargs: Any) -> None:
|
|
||||||
"""兼容 ApiRequestContext 接口"""
|
|
||||||
self.audit_metadata.update(kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def _lazy_create_adapter(class_name: str, **kwargs: Any) -> Any:
|
|
||||||
"""从 src.api.dashboard.routes 动态加载 Adapter 类并实例化。
|
|
||||||
|
|
||||||
避免 services→api 的静态 import 依赖。
|
|
||||||
"""
|
|
||||||
routes = importlib.import_module("src.api.dashboard.routes")
|
|
||||||
adapter_cls = getattr(routes, class_name)
|
|
||||||
return adapter_cls(**kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class CacheWarmupService:
|
|
||||||
"""缓存预热服务"""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
async def warmup_all(cls, delay_seconds: float = 3.0) -> None:
|
|
||||||
"""
|
|
||||||
预热所有关键缓存
|
|
||||||
|
|
||||||
Args:
|
|
||||||
delay_seconds: 启动后延迟执行的秒数,确保系统完全就绪
|
|
||||||
"""
|
|
||||||
await asyncio.sleep(delay_seconds)
|
|
||||||
|
|
||||||
logger.info("开始预热关键缓存...")
|
|
||||||
start_time = time.time()
|
|
||||||
|
|
||||||
results = await asyncio.gather(
|
|
||||||
cls._warmup_admin_dashboard_stats(),
|
|
||||||
cls._warmup_admin_heatmap(),
|
|
||||||
cls._warmup_daily_stats(),
|
|
||||||
return_exceptions=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
success_count = sum(1 for r in results if r is True)
|
|
||||||
error_count = sum(1 for r in results if isinstance(r, Exception))
|
|
||||||
elapsed = time.time() - start_time
|
|
||||||
|
|
||||||
if error_count > 0:
|
|
||||||
logger.warning(
|
|
||||||
"缓存预热完成: {}/{} 成功, {} 失败, 耗时 {:.2f}s",
|
|
||||||
success_count,
|
|
||||||
3,
|
|
||||||
error_count,
|
|
||||||
elapsed,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
logger.info("缓存预热完成: {}/{} 成功, 耗时 {:.2f}s", success_count, 3, elapsed)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
async def _warmup_admin_dashboard_stats(cls) -> bool:
|
|
||||||
"""预热管理员仪表盘统计缓存"""
|
|
||||||
db = None
|
|
||||||
try:
|
|
||||||
from src.models.database import User as DBUser
|
|
||||||
|
|
||||||
db = create_session()
|
|
||||||
|
|
||||||
# 获取一个管理员用户用于构造 context
|
|
||||||
admin_user = db.query(DBUser).filter(DBUser.role == UserRole.ADMIN).first()
|
|
||||||
if not admin_user:
|
|
||||||
logger.info("缓存预热: 无管理员用户,跳过仪表盘统计预热")
|
|
||||||
return True
|
|
||||||
|
|
||||||
context = WarmupContext(db=db, user=admin_user)
|
|
||||||
adapter = _lazy_create_adapter("AdminDashboardStatsAdapter")
|
|
||||||
await adapter.handle(context)
|
|
||||||
|
|
||||||
logger.debug("缓存预热: 管理员仪表盘统计已预热")
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning("缓存预热失败 (仪表盘统计): {}", e)
|
|
||||||
return False
|
|
||||||
finally:
|
|
||||||
if db:
|
|
||||||
db.close()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
async def _warmup_admin_heatmap(cls) -> bool:
|
|
||||||
"""预热管理员热力图缓存"""
|
|
||||||
db = None
|
|
||||||
try:
|
|
||||||
from src.services.usage.service import UsageService
|
|
||||||
|
|
||||||
db = create_session()
|
|
||||||
|
|
||||||
# 预热全局热力图(管理员视角)
|
|
||||||
await UsageService.get_cached_heatmap(
|
|
||||||
db=db,
|
|
||||||
user_id=None,
|
|
||||||
include_actual_cost=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.debug("缓存预热: 管理员热力图已预热")
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning("缓存预热失败 (热力图): {}", e)
|
|
||||||
return False
|
|
||||||
finally:
|
|
||||||
if db:
|
|
||||||
db.close()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
async def _warmup_daily_stats(cls) -> bool:
|
|
||||||
"""预热每日统计缓存"""
|
|
||||||
db = None
|
|
||||||
try:
|
|
||||||
from src.models.database import User as DBUser
|
|
||||||
|
|
||||||
db = create_session()
|
|
||||||
|
|
||||||
# 获取一个管理员用户
|
|
||||||
admin_user = db.query(DBUser).filter(DBUser.role == UserRole.ADMIN).first()
|
|
||||||
if not admin_user:
|
|
||||||
logger.info("缓存预热: 无管理员用户,跳过每日统计预热")
|
|
||||||
return True
|
|
||||||
|
|
||||||
context = WarmupContext(db=db, user=admin_user)
|
|
||||||
|
|
||||||
# 预热 7 天的每日统计
|
|
||||||
adapter = _lazy_create_adapter("DashboardDailyStatsAdapter", days=7)
|
|
||||||
await adapter.handle(context)
|
|
||||||
|
|
||||||
logger.debug("缓存预热: 每日统计已预热")
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning("缓存预热失败 (每日统计): {}", e)
|
|
||||||
return False
|
|
||||||
finally:
|
|
||||||
if db:
|
|
||||||
db.close()
|
|
||||||
|
|
||||||
|
|
||||||
async def start_cache_warmup() -> None:
|
|
||||||
"""启动缓存预热(作为后台任务)"""
|
|
||||||
if not config.cache_warmup_enabled:
|
|
||||||
logger.info("缓存预热已禁用(CACHE_WARMUP_ENABLED=false)")
|
|
||||||
return
|
|
||||||
|
|
||||||
asyncio.create_task(CacheWarmupService.warmup_all())
|
|
||||||
@@ -5,54 +5,11 @@ from types import SimpleNamespace
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import src.services.system.cache_warmup as cache_warmup_module
|
|
||||||
import src.services.system.maintenance_scheduler as maintenance_scheduler_module
|
import src.services.system.maintenance_scheduler as maintenance_scheduler_module
|
||||||
from src.config.settings import config
|
from src.config.settings import config
|
||||||
from src.services.system.maintenance_scheduler import MaintenanceScheduler
|
from src.services.system.maintenance_scheduler import MaintenanceScheduler
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_start_cache_warmup_skips_task_when_disabled(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
||||||
monkeypatch.setattr(config, "cache_warmup_enabled", False)
|
|
||||||
|
|
||||||
created = False
|
|
||||||
|
|
||||||
def fake_create_task(coro): # type: ignore[no-untyped-def]
|
|
||||||
nonlocal created
|
|
||||||
created = True
|
|
||||||
if inspect.iscoroutine(coro):
|
|
||||||
coro.close()
|
|
||||||
return object()
|
|
||||||
|
|
||||||
monkeypatch.setattr(cache_warmup_module.asyncio, "create_task", fake_create_task)
|
|
||||||
|
|
||||||
await cache_warmup_module.start_cache_warmup()
|
|
||||||
|
|
||||||
assert created is False
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_start_cache_warmup_creates_task_when_enabled(
|
|
||||||
monkeypatch: pytest.MonkeyPatch,
|
|
||||||
) -> None:
|
|
||||||
monkeypatch.setattr(config, "cache_warmup_enabled", True)
|
|
||||||
|
|
||||||
created = False
|
|
||||||
|
|
||||||
def fake_create_task(coro): # type: ignore[no-untyped-def]
|
|
||||||
nonlocal created
|
|
||||||
created = True
|
|
||||||
if inspect.iscoroutine(coro):
|
|
||||||
coro.close()
|
|
||||||
return object()
|
|
||||||
|
|
||||||
monkeypatch.setattr(cache_warmup_module.asyncio, "create_task", fake_create_task)
|
|
||||||
|
|
||||||
await cache_warmup_module.start_cache_warmup()
|
|
||||||
|
|
||||||
assert created is True
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_maintenance_scheduler_start_skips_startup_task_when_disabled(
|
async def test_maintenance_scheduler_start_skips_startup_task_when_disabled(
|
||||||
monkeypatch: pytest.MonkeyPatch,
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
|||||||
Reference in New Issue
Block a user