Skip to content

cli/driver-sql: os migrate plan 自称 dry-run,却仍会在全新项目上创建空数据库文件(#6469 的残余写副作用) #6743

Description

@os-project-manager

发现于 #6469 的实施(PR 见下方链接)。#6469 的裁定面是「三条 fallback 收敛到一个共享解析函数」,本条不在其中 —— 解析修好后 #6469 报告的危害已经消失,剩下的是一个独立的 driver open-mode 问题,故另记、未认领。

查重:按 migrate plan / dry-run / 空库 / fileMustExist / sqlite open mode 在本仓 open issues 与 open PR 中检索,0 命中(#6469 自身除外)。

现象

os migrate plan 自称 dry-run,#3917 之后连 boot 时的建表 DDL 与 artifact seed 都改成了「报告而不执行」。但它仍然会在磁盘上创建数据库文件本身 —— sqlite driver 以默认的「不存在就建」模式打开目标路径。

#6469 修复之前,这条是那个 bug 的放大器:plan 解析到一个谁都没在用的 standalone.db,把它建出来,然后在这个空库上报告全量 drift;第二次跑,磁盘上已经真有这么个文件,假报告看起来更像既定事实。

#6469 落地后,plan 指向的是服务真正在用的库,所以原报告的那条危害路径已经死了。剩下的残余是:在一个全新的、还没跑过任何东西的项目里跑 os migrate plan,会留下一个 0 表的 .objectstack/data/objectstack.db(以及可能的 -wal/-shm)。

复现(全新项目,从未 start/dev 过):

os build
ls .objectstack/data/ 2>/dev/null   # 不存在
os migrate plan
ls .objectstack/data/               # 多出 objectstack.db —— 一个只读命令的写副作用

为什么仍值得记一笔

危害等级比 #6469 低得多(不再有假报告,只是一个空文件),但性质没变:一个声明为 dry-run 的命令留下了写副作用。它同时让「这个项目还没有数据库」这个状态变得不可区分 —— 下一个命令看到文件存在,就不会再走首次初始化的判断。

修复方向(未裁)

核心问题是 sqlite driver 的 open 模式在「只读探测」与「正常 boot」之间没有区分:

  • AbootSchemaStack({ deferSchemaDdl: true }) 这条路径一个 read-only / fileMustExist 语义,目标文件不存在时不建,而是明确报告「这个项目还没有数据库,plan 无从 diff」([17.0.0-rc.0] os migrate apply: no occupancy/lock detection for SQLite, and boot-time DDL runs before the confirmation prompt #3917 已经确立了 defer 这条路径,这里是把 defer 从 DDL 延伸到 open 本身);
  • B 在 CLI 层先 existsSync 探一次,不存在就短路成一条说明性输出,不 boot stack。代价是这个判断会与 driver 真正的 open 语义分成两处,容易漂移;
  • C 不动 open 模式,只在 plan 结束时清理自己刚建的空文件。最不推荐 —— 「建了再删」在崩溃/中断时留下残骸,而且掩盖了根因。

倾向 A(修在 driver 的能力边界上,一处语义),但这牵涉 @objectstack/driver-sql 的公开 open 行为,应由维护者裁。

相关:#3917(defer DDL / 占用守卫)、#6469(默认库解析统一)。

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions