fix(db): 增强数据库会话管理的容错性

调整了 `get_db_session` 的行为,当数据库未能成功初始化时,它现在会返回 `None` 并记录错误,而不是抛出异常。这提高了应用在数据库连接不可用时的健壮性,避免了程序因无法获取会话而崩溃。

- `VideoAnalyzer` 已更新,增加了对会话为 `None` 的检查,以安全地跳过数据库读写操作。
- 附带对 `VideoAnalyzer` 和 `LegacyVideoAnalyzer` 进行了重构,将模型选择和API请求执行的逻辑抽象到独立的 `_model_selector` 和 `_executor` 组件中,提升了代码的清晰度和可维护性。
This commit is contained in:
tt-P607
2025-10-01 06:04:07 +08:00
parent 7bc510b96c
commit 67aa936013
3 changed files with 36 additions and 16 deletions

View File

@@ -207,6 +207,9 @@ class VideoAnalyzer:
"""检查视频是否已经分析过"""
try:
async with get_db_session() as session:
if not session:
logger.warning("无法获取数据库会话,跳过视频存在性检查。")
return None
# 明确刷新会话以确保看到其他事务的最新提交
await session.expire_all()
stmt = select(Videos).where(Videos.video_hash == video_hash)
@@ -227,6 +230,9 @@ class VideoAnalyzer:
try:
async with get_db_session() as session:
if not session:
logger.warning("无法获取数据库会话,跳过视频结果存储。")
return None
# 只根据video_hash查找
stmt = select(Videos).where(Videos.video_hash == video_hash)
result = await session.execute(stmt)
@@ -540,11 +546,14 @@ class VideoAnalyzer:
# logger.info(f"✅ 多帧消息构建完成,包含{len(frames)}张图片")
# 获取模型信息和客户端
model_info, api_provider, client = self.video_llm._select_model()
selection_result = self.video_llm._model_selector.select_best_available_model(set(), "response")
if not selection_result:
raise RuntimeError("无法为视频分析选择可用模型。")
model_info, api_provider, client = selection_result
# logger.info(f"使用模型: {model_info.name} 进行多帧分析")
# 直接执行多图片请求
api_response = await self.video_llm._execute_request(
api_response = await self.video_llm._executor.execute_request(
api_provider=api_provider,
client=client,
request_type=RequestType.RESPONSE,

View File

@@ -461,11 +461,14 @@ class LegacyVideoAnalyzer:
# logger.info(f"✅ 多帧消息构建完成,包含{len(frames)}张图片")
# 获取模型信息和客户端
model_info, api_provider, client = self.video_llm._select_model()
selection_result = self.video_llm._model_selector.select_best_available_model(set(), "response")
if not selection_result:
raise RuntimeError("无法为视频分析选择可用模型 (legacy)。")
model_info, api_provider, client = selection_result
# logger.info(f"使用模型: {model_info.name} 进行多帧分析")
# 直接执行多图片请求
api_response = await self.video_llm._execute_request(
api_response = await self.video_llm._executor.execute_request(
api_provider=api_provider,
client=client,
request_type=RequestType.RESPONSE,

View File

@@ -759,30 +759,38 @@ async def initialize_database():
@asynccontextmanager
async def get_db_session() -> AsyncGenerator[AsyncSession, None]:
"""异步数据库会话上下文管理器"""
async def get_db_session() -> AsyncGenerator[Optional[AsyncSession], None]:
"""
异步数据库会话上下文管理器。
在初始化失败时会yield None调用方需要检查会话是否为None。
"""
session: Optional[AsyncSession] = None
SessionLocal = None
try:
engine, SessionLocal = await initialize_database()
_, SessionLocal = await initialize_database()
if not SessionLocal:
raise RuntimeError("Database session not initialized")
session = SessionLocal()
logger.error("数据库会话工厂 (_SessionLocal) 未初始化。")
yield None
return
except Exception as e:
logger.error(f"数据库初始化失败,无法创建会话: {e}")
yield None
return
try:
session = SessionLocal()
# 对于 SQLite在会话开始时设置 PRAGMA
from src.config.config import global_config
if global_config.database.database_type == "sqlite":
try:
await session.execute(text("PRAGMA busy_timeout = 60000"))
await session.execute(text("PRAGMA foreign_keys = ON"))
except Exception as e:
logger.warning(f"[SQLite] 设置会话 PRAGMA 失败: {e}")
await session.execute(text("PRAGMA busy_timeout = 60000"))
await session.execute(text("PRAGMA foreign_keys = ON"))
yield session
except Exception as e:
logger.error(f"数据库会话错误: {e}")
logger.error(f"数据库会话期间发生错误: {e}")
if session:
await session.rollback()
raise
raise # 将会话期间的错误重新抛出给调用者
finally:
if session:
await session.close()