Skip to content

[verify] bootStack({ databaseFile }) 的数据活不过进程内重启 —— 表在、行没了,挡住冷启动类 e2e #4518

Description

@os-zhuang

在做 #4470(让验证 harness 走真实持久化路径)时发现的。它挡住了 #4470「最小形态」的第三条,所以单独记录 —— packages/qa/dogfood/test/flow-durable-suspend.dogfood.test.ts 的文件头、以及 @objectstack/verify 的 changeset 都引用了这条。

问题

#4470bootStack 加了 databaseFile,本意是让两次顺序启动共享同一个 SQLite 文件,从而构成一次真正的冷启动(ADR-0019 的挂起 run 必须活过的那种重启)。实际行为是:第二次 bootStack 打开同一个文件,表结构在,行没了

关键点是这与挂起 run 无关 —— 普通业务记录同样不保留。所以归因在 harness/驱动这一层,不在挂起存储。

复现

constdir=mkdtempSync(join(tmpdir(),'probe-'));constdbFile=join(dir,'v.sqlite');consta=awaitbootStack(someStack,{automation: true,databaseFile: dbFile});constta=awaita.signIn();awaita.apiAs(ta,'POST','/data/suspend_note',{name: 'persisted-me',status: 'new'});awaita.stop();// kernel shutdownconstb=awaitbootStack(someStack,{automation: true,databaseFile: dbFile});consttb=awaitb.signIn();awaitb.apiAs(tb,'GET','/data/suspend_note?limit=10');

实测输出:

PROBE hot row status 200 ← 第一个进程里读得到
PROBE file exists true 1073152 ← 文件确实在,1MB
PROBE cold automation_run {"records":[],"total":0}
PROBE cold suspend_note {"records":[],"total":0} ← 普通记录也没了

两次运行文件大小完全一致(1073152),像是在某个早期时点(schema sync 之后)写过一次就再没更新。表能查到 200(不是 no such table),说明第二次启动确实读到了那个文件 —— 只是里面没有后来写入的行。

线索

  • packages/plugins/driver-sqlite-wasm/src/sqlite-wasm-driver.ts —— persist 默认 'on-disconnect':只在驱动 disconnect 时把 sql.js 的内存镜像刷回磁盘(外加一个 process.once('beforeExit') 的兜底 flush)。
  • packages/runtime/src/default-datasource-plugin.ts —— destroy()connection.disconnect('default', { asDefault: true }),看起来应该触发那次 flush。
  • packages/verify/src/harness.ts —— stop() 关掉 http server 后调用 kernel.shutdown();日志里确实出现 Graceful shutdown started / ✅ Graceful shutdown complete

所以从代码看这条链路本该 flush,但结果表明没有(或者 flush 发生得太早)。没有继续深挖,因为这已经在 #4470 的范围之外了。

注意 beforeExit 兜底对进程内重启是无效的:进程根本没退出。objectstack dev 这类「跑到被杀」的用法因此不受影响,这也可能是它一直没被发现的原因。

影响

  1. 挡住 [verify] 端到端验证从构造上绕开了挂起 run 的持久化路径(harness 写死 suspendedRunStore: 'memory') #4470 的第三条最小形态 —— 「冷启动一个新 kernel,resume 能继续走对分支」目前无法在 dogfood 层断言。已在测试文件头标注为 KNOWN GAP,没有用放松断言的方式假装通过。
  2. 任何想验证「跨进程状态存活」的 e2e 都会撞上同一堵墙 —— 而这正是持久化类缺陷([automation/approvals] 进程重启后审批决策静默失效:挂起 flow run 仍只存内存(#1518 标记 COMPLETED 但 17.0.0-rc.1 未生效),approve 落库却永不推进且零报错 #4420 那一类)最需要被覆盖的维度。

期望

bootStack({ databaseFile }) 之后的 stop() 应当保证数据已落盘,使下一次 bootStack 是一次真实的冷启动。可能的方向(未验证,留给认领者判断):

  • 查清 stop()kernel.shutdown() → 插件 destroy() → 驱动 disconnect() 这条链在 harness 里是否真的走全了;
  • 或让 harness 在 stop() 里显式调用驱动已有的 flush()
  • 或在 databaseFile 模式下改用更即时的 persist 策略(如 debounced:*)。

修好之后,flow-durable-suspend.dogfood.test.ts 最自然的下一个用例就是它最初被写出来的那个:挂起 → 关停 → 冷启动 → resume 走对分支。

参考

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions