Skip to content

fix(cli,runtime): os dev / os start / os migrate 解析出三个不同的默认数据库 —— migrate plan 因此在自己刚建的空库上报告全量 drift #6469

Description

@os-zhuang

现象

在一个装好的 app 项目里(hotcrm,17.0.0-rc.5),先 objectstack start 跑起服务并完成 seed,然后按 docs/MAINTENANCE.md §3.1 的升级后检查跑 os migrate plan,得到:

ℹ Database: /home/user/hotcrm/.objectstack/data/standalone.db
ℹ Examined 22 managed table(s).

New (additive — created when you apply)
  + crm_account [create_table, 30 column(s)]
  + crm_campaign [create_table, 29 column(s)]
  ... 22 张表全部待创建
ℹ 22 table(s) to create, 0 column(s) to add

这份报告是假的。 显式指向服务真正在用的库之后:

$ os migrate plan --database-url file:/home/user/hotcrm/.objectstack/data/objectstack.db
ℹ Examined 22 managed table(s).
✓ Physical schema is in sync with metadata — nothing to migrate.

零漂移。plan 打开的是一个它自己刚刚创建的空库

$ ls -la .objectstack/data/
-rw-r--r-- 2629632 Aug  7 12:02 objectstack.db    ← 服务在用的
-rw-r--r--    4096 Aug  7 12:01 standalone.db     ← plan 刚建的空库

根因:三个命令,三个写死的默认库名,同一个目录

命令 默认数据库 出处
os dev <projectRoot>/.objectstack/data/**dev.db** packages/cli/src/commands/dev.ts:37resolveDefaultDevDbUrl,用于 dev.ts:301,由 dev-default-db.test.ts 钉住)
os start <projectRoot>/.objectstack/data/**objectstack.db** packages/cli/src/commands/start.ts:193
os migrate * <projectRoot>/.objectstack/data/**standalone.db** packages/runtime/src/standalone-stack.ts:222resolveDatabaseUrl

三条路径谁都不从 objectstack.config.ts 解析数据源,各自 fallback 到一个不同的硬编码文件名,而且三个文件落在同一个目录里。

这不是「独立工具不认识你的项目」:migrate plan 明明读了项目配置 —— 它把整个应用的元数据注册表都加载了([Registry] Registered data: app.objectstack.hotcrm:crm_account 等 20 余行)。它读了 A 的元数据定义,然后拿去 diff B 的物理现状。

二阶损害:#3917 的占用检查守着一个不会被占用的文件

packages/cli/src/utils/sqlite-occupancy.ts:9 的动机注释写的是:

the overwhelmingly common shape of a dev machine is a pnpm dev server holding the same .objectstack/data/standalone.db open while the operator runs a migration in another terminal

这个前提在默认路径下不成立pnpm devos dev)持有的是 dev.dbos migrate apply 打开的是 standalone.db。两者在默认配置下永远不会争用同一个文件,所以 #3917 那套 /proc / lsof 占用检测在它自己描述的那个主场景里不可能触发。

(守卫本身不是废的 —— 如果操作者显式把 OS_DATABASE_URL 指到同一个文件,它照常工作。失效的是它的默认路径场景,也就是注释里说的「压倒性常见」的那个。)

为什么这条比「输出看着怪」严重

失败方向是反的。 真相是「零漂移」,输出是「22 张表全部待创建」。它不会让人漏掉问题,它会让人以为升级把数据库干掉了。照着 §3.1 走的操作者,最可能的下一步是去回滚一个根本没坏的东西。

它有写副作用。 plan 号称 dry-run,但解析路径上的空库会被建出来。跑第二次看到的还是同一份假报告,而且现在磁盘上真有这么个文件,更像「就该是这样」。

它正在长配套逻辑。 sqlite-occupancy.ts 已经把 standalone.db 当成事实写进了注释和实现。这类分叉每多待一个版本,就多一处把它当既定行为的代码。

给操作者的判别式(在修好之前)

看到 os migrate plan 报「所有表都待创建」而应用明明在正常跑 —— 那是指错库了,不是数据库坏了。确认办法是看输出第一行的 ℹ Database: 路径是不是服务实际在用的那个(os start 启动横幅里的 Driver: ... → <path>)。

显式指定时注意 scheme 只认 file:

os migrate plan --database-url file:/abs/path/.objectstack/data/objectstack.db

sqlite:// 会被拒(Unsupported database URL scheme ... Supported schemes: memory://, postgres://, pg://, mongodb://, mongodb+srv://, file:)。错误信息本身合格,但 sqlite:// 大概是这个场景下最自然的猜测,值得一并考虑接受。

修复方向(供讨论,未认领)

核心是「一个项目的数据库」应当只有一处解析。倾向于把三条 fallback 收敛到一个共享函数,并让它优先读项目配置声明的数据源;默认文件名统一(保留其余两个作为兼容读取,或在检测到旧文件时给出迁移提示,避免既有 dev 环境的数据凭空「消失」)。

兼容性需要考虑:已经存在 dev.db / standalone.db 的本地环境,统一之后不能表现为数据丢失。

修好后建议补的钉子:一条断言 dev / start / migrate 在同一 projectRoot 下解析出同一个 URL 的测试 —— 这正是现有 dev-default-db.test.ts 只钉住自己那一条、因而没能发现分叉的原因。

复现

# 任一 objectstack app 项目内
objectstack build && objectstack start      # 记下横幅里的 Driver 路径
# 另一个终端
os migrate plan                             # 报告全量 create_table
ls .objectstack/data/                       # 多出一个空的 standalone.db

发现于 hotcrm 升级到 17.0.0-rc.5 时(objectstack-ai/hotcrm#1056)走 MAINTENANCE.md §3.1 的升级后漂移检查。仅记录,未认领。

Metadata

Metadata

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions