fix(init_db): await 优先级漏括号——启动迁移自诞生起从未执行成功(事故根因)

has_alembic = await conn.execute(...).scalar()  因 await 优先级低于
属性访问,实际解析为 await (conn.execute(...).scalar())——在协程对象
上调 .scalar() 必抛 AttributeError('coroutine' object has no attribute
'scalar')。启动迁移每次容器启动都在第一行炸掉,再被 except 吞成一行
日志(4a5dc61 已改为 logger.exception)。两行均改为
(await conn.execute(...)).scalar()。

已于生产库实测:主库路径(has_alembic=True→upgrade no-op)与临时
历史库路径(stamp+upgrade)全部跑通,临时库已删除。

至此 2026-09-26 事故根因链闭合:启动迁移从未生效 → prod schema 长期
落后于模型 → 新镜像上线即上传 500(缺 product_id)。

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
2026-09-26 23:17:33 +08:00
parent 4a5dc61a02
commit aaaac95b53
+7 -3
View File
@@ -35,11 +35,15 @@ async def _run_alembic_migrations() -> None:
- 既有但无 alembic_version(历史 DB):先 stamp head 基线,再 upgrade(no-op) - 既有但无 alembic_version(历史 DB):先 stamp head 基线,再 upgrade(no-op)
""" """
async with db_manager.engine.begin() as conn: async with db_manager.engine.begin() as conn:
has_alembic = await conn.execute(text("SELECT to_regclass('public.alembic_version')")).scalar() # 注意括号:await 优先级低于属性访问,await conn.execute(...).scalar()
# 实际是 await (conn.execute(...).scalar())——会在协程对象上调 .scalar()
# 直接 AttributeError。必须 (await conn.execute(...)).scalar()。
# 曾因漏括号让启动迁移自诞生起一次都没跑通过(2026-09-26 事故根因)。
has_alembic = (await conn.execute(text("SELECT to_regclass('public.alembic_version')"))).scalar()
if not has_alembic: if not has_alembic:
table_count = await conn.execute( table_count = (await conn.execute(
text("SELECT count(*) FROM information_schema.tables WHERE table_schema='public' AND table_name <> 'alembic_version'") text("SELECT count(*) FROM information_schema.tables WHERE table_schema='public' AND table_name <> 'alembic_version'")
).scalar() )).scalar()
if table_count and table_count > 0: if table_count and table_count > 0:
logger.info("检测到既有 DB 未纳入 alembic 管理,自动 stamp head 作为基线") logger.info("检测到既有 DB 未纳入 alembic 管理,自动 stamp head 作为基线")
await asyncio.to_thread(_alembic_stamp_head) await asyncio.to_thread(_alembic_stamp_head)