现象
在一个装好的 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:37(resolveDefaultDevDbUrl,用于 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:222(resolveDatabaseUrl) |
三条路径谁都不从 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 dev(os dev)持有的是 dev.db,os 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 的升级后漂移检查。仅记录,未认领。
现象
在一个装好的 app 项目里(hotcrm,17.0.0-rc.5),先
objectstack start跑起服务并完成 seed,然后按docs/MAINTENANCE.md§3.1 的升级后检查跑os migrate plan,得到:这份报告是假的。 显式指向服务真正在用的库之后:
零漂移。
plan打开的是一个它自己刚刚创建的空库:根因:三个命令,三个写死的默认库名,同一个目录
os dev<projectRoot>/.objectstack/data/**dev.db**packages/cli/src/commands/dev.ts:37(resolveDefaultDevDbUrl,用于dev.ts:301,由dev-default-db.test.ts钉住)os start<projectRoot>/.objectstack/data/**objectstack.db**packages/cli/src/commands/start.ts:193os migrate *<projectRoot>/.objectstack/data/**standalone.db**packages/runtime/src/standalone-stack.ts:222(resolveDatabaseUrl)三条路径谁都不从
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的动机注释写的是:这个前提在默认路径下不成立。
pnpm dev(os dev)持有的是dev.db,os 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::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只钉住自己那一条、因而没能发现分叉的原因。复现
发现于 hotcrm 升级到 17.0.0-rc.5 时(objectstack-ai/hotcrm#1056)走
MAINTENANCE.md§3.1 的升级后漂移检查。仅记录,未认领。