Skip to content

每个 os migrate 子命令关停时,悬空引用巡检都会把 sys_metadata / sys_view_definition 报成 unreadableObjects(连接已关闭) #4747

Description

@os-zhuang

发现自 #4556 的实现过程(PR #4742)跑真实 CLI 时。未认领。与 #4556 无关,任何 bootSchemaStack 系的子命令都能复现。

复现

cd examples/app-crm
OS_DATABASE_URL="file:/tmp/t.db" node packages/cli/bin/run.js migrate recorded-by --json

命令正常返回 JSON、退出码正确。但在返回之后、进程关停时,稳定打出:

ERROR Find operation failed {"object":"sys_metadata","error":{"message":"Unable to acquire a connection", …}}
WARN  [integrity] dangling-reference audit could not list an object {"object":"sys_metadata","error":"Unable to acquire a connection"}
ERROR Find operation failed {"object":"sys_view_definition","error":{"message":"Unable to acquire a connection", …}}
WARN  [integrity] dangling-reference audit could not list an object {"object":"sys_view_definition","error":"Unable to acquire a connection"}
WARN  [integrity] stored references that resolve to nothing (#4551)
      {"scanned":0,"dangling":0,"undetermined":0,
       "unreadableObjects":["sys_metadata","sys_view_definition"],
       "truncatedObjects":[],"references":[]}

#4551 的巡检在 stack shutdown 之后才跑到这两个对象,此时 knex 连接池已经关了。

为什么值得修,而不只是噪音

巡检自己的文档把这件事写成了它最在意的一条设计原则:

Unknown and absent are DIFFERENT answers

The single most important property here. An existence probe that cannot run … must never be recorded as "the target does not exist" … Together they are what stops "0 dangling" from ever being read as "everything is fine".

unreadableObjects 这个桶存在的意义,正是让「我没能检查」区别于「我检查了,没问题」。而现在它在每一次正常的 CLI 调用里都非空 —— 一个恒定为真的告警不再携带任何信息,它训练操作者忽略这个桶。等到真有一个对象因为数据源故障读不出来时,那条信息会淹没在同样的两行里。

这也正是 AGENTS.md「Route & surface ownership」第 3 条要防的形态:

3. Absence must be loud. … a verifier that silently degrades (reusing a stale build, skipping a check it could not run) is worse than no verifier, because it reports success.

这里是它的镜像:一个永远报告自己降级了的巡检,与不报告一样没有信息量。

另外那两行 ERROR Find operation failed 是引擎层打的,级别是 ERROR,出现在一条成功的命令输出里 —— 对 CI 日志抓 ERROR 的流水线是直接的误报源。

可能的方向(未验证,需要看巡检的触发时机)

  • A. 巡检在 shutdown 之前跑完,或在 shutdown 时跳过。 一次性 CLI 进程本来就不该在关停路径上做完整巡检 —— 它的定位是启动期/巡检期的可观测性,不是退出钩子。
  • B. 关停中的连接失败不进 unreadableObjects 把「引擎正在关停」与「数据源不可达」区分开:前者不是一个发现,不该占用那个桶。
  • C. 一次性命令进程默认不跑这个巡检(由 os serve / 巡检专用命令承担)。

倾向 A + B:A 解决时序,B 保证即使时序再次被打破,这个桶也不会被非发现污染。C 单独用的话会让巡检在 CLI 场景永久失明,与它的存在意义相悖。

边界

Refs #4551#4556(PR #4742)、#4743

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions