后端-ORM-execution_options在asyncmy中返回coroutine导致500
问题现象
- 网页新闻列表页
/#/web-news显示空白,无任何数据 - 浏览器控制台报错:
GET /api/web-crawler/news?page=1&page_size=20 500 (Internal Server Error) - 前端日志:
AxiosError: Request failed with status code 500
核心问题
- 根因:
SQLAlchemy AsyncConnection.execution_options(isolation_level="SERIALIZABLE")在 asyncmy (MySQL 异步驱动) 中返回 coroutine 对象而非 AsyncConnection,导致后续conn.begin()调用报错:AttributeError: 'coroutine' object has no attribute 'begin' - 触发代码(
modules/web-crawler/service.py:155-158):conn = await session.connection() conn = conn.execution_options(isolation_level="SERIALIZABLE") # asyncmy 下返回 coroutine async with conn.begin(): # 此处崩溃 - 与 SQLite 的差异:本地开发用 SQLite + aiosqlite 驱动,相同代码正常工作;服务器用 MySQL + asyncmy 驱�动,
execution_options行为不同 - 数据库配置:服务器
DB_HOST=localhost、DB_PORT=3306、MySQL - 影响 commit:
2b2ad7e(前一个修复引入的问题)
解决方法
- 去掉
execution_options+conn.begin()包装,恢复使用session.execute()直接查询 - 保留
order_by(desc(WebNews.id))— id 不可变排序键是解决分页漂移的核心修复,不依赖事务隔离级别 - MySQL 默认
REPEATABLE READ隔离级别已保证同一事务内读取一致性
修复后的代码(modules/web-crawler/service.py):
# 直接使用 session.execute(),无需 execution_options
count_stmt = select(func.count()).select_from(base_stmt.subquery())
total_result = await session.execute(count_stmt)
total = total_result.scalar() or 0
stmt = base_stmt.order_by(desc(WebNews.id))
stmt = stmt.offset((page - 1) * page_size).limit(page_size)
result = await session.execute(stmt)
items = list(result.scalars().all())
- 部署:
git push→ 服务器git pull→systemctl restart news-backend
下次防错措施
- 快速定位:查看
journalctl -u news-backend -n 20搜索execution_options或coroutine关键字 - 驱动兼容性检查:SQLAlchemy ORM 涉及
execution_options的代码需在 MySQL + asyncmy 环境下验证,不能仅依赖 SQLite 本地测试 - 部署前建议:在
start.bat或部署脚本中增加curl http://localhost:8000/api/web-crawler/news?page=1&page_size=1的冒烟测试 - 代码规范:避免在异步 Session 上使用
session.connection()+conn.begin()嵌套事务;Session 自身的自动事务管理通常已足够