fix(metadata-protocol): loadMetaFromDb 判「表未建」改问 isMissingTableError,退休手抄的 /no such table/i (#5841) - #5889
Merged
Conversation
…休手抄的 /no such table/i (#5841) `loadMetaFromDb` 的外层 catch 靠自己的 `/no such table/i` 正则判断一次失败的 `sys_metadata` 读是不是「首启表还没建」这个良性原因。这是同一个文件里的第二份 手抄词表:本文件 `:15` 已经 import 了 `isMissingTableError`, `rethrowUnlessMetadataStoreUnprovisioned`(#5532 / PR #5705)、本包 `SysMetadataRepository`(#4867)、`DatabaseLoader`(#5108)问的都是它。 手抄的一份两个方向都错,而且只有一个方向是响的: - SQLite 说 `no such table: sys_metadata`,正则碰巧匹配; - PostgreSQL 说 `relation "sys_metadata" does not exist`(SQLSTATE 42P01), MySQL 说 `Table 'app.sys_metadata' doesn't exist`(errno 1146),两者都不匹配 —— 于是一次完全健康的首启会打出 `[Protocol] DB hydration skipped: …`, 一条运维无从下手的告警; - 反向:任何驱动把别的失败也写成 "no such table" 时被误判为良性、无声吞掉。 改为按错误类型问 `isMissingTableError`(code / errno / message / 一层 cause), 一个驱动怪癖只教平台一遍。顺带:warn 行改用 `e instanceof Error ? e.message : String(e)`,非 Error 的 rejection 不再打印 `undefined`。 本单只做事实 1。事实 2(非良性失败仍是 `console.warn` + `{loaded:0}` 返回, 调用方无法把「库里没有 overlay」与「根本没读到库」分开,ADR-0110 D3 落在 boot 侧) 只测量、只报告:改它要动本方法的返回契约与 `ObjectQLPlugin.restoreMetadataFromDb`, 不与本改动绑成一次。 新测试文件 `protocol.load-meta-hydration-benign.test.ts`(10 例)覆盖 sqlite / Postgres 措辞 / 42P01 / errno 1146 / cause 链五种「表未建」形状均判良性无告警, ECONNREFUSED 与非 Error rejection 仍走告警分支,以及工作正常的库不受影响。 其中的假引擎只声明 `find`(`loadMetaFromDb` 只调它),不声明任何写动词 —— 本包不能 import `@objectstack/objectql`(反向依赖成环),也就不手抄谓词。 反向验证(先预测后运行):恢复正则版本 → 5 红 5 绿,红的恰是 Postgres 措辞、 42P01、errno 1146、cause 链与非 Error rejection 四加一例,绿的是 sqlite 措辞、 ECONNREFUSED、良性过宽守卫、fact-2 等价性与工作库对照 —— 与预测逐条一致。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Q7oc7ASjh8yxyS3Yz78We
…d-meta-benign-predicate
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 3 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
This was referenced Aug 6, 2026
baozhoutao
marked this pull request as ready for review
August 6, 2026 11:12
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #5841
前提核验(origin/main,实读)
派发前先按 Prime Directive #6 核前提,成立:
packages/metadata-protocol/src/protocol.ts的/no such table/i正则仍在(分诊时锚在 :10352,今天 5 次合并后漂到 :10443-10444,以内容定位);:15已import { isMissingTableError } from '@objectstack/metadata/errors',:3162的rethrowUnlessMetadataStoreUnprovisioned正在用它,另有 6 处调用点。⇒ 「同文件已有集中谓词,这里却手抄第二份」属实,且零 import 成本。改了什么(issue 事实 1)
loadMetaFromDb外层 catch 从「拿e.message跑自己的正则」改为「问isMissingTableError(e)」。手抄的一份两个方向都错,而且只有一个方向是响的:
no such table: sys_metadatarelation "sys_metadata" does not exist(SQLSTATE42P01)[Protocol] DB hydration skipped: …Table 'app.sys_metadata' doesn't exist(errno 1146)isMissingTableError按 code / errno / message / 一层cause认定,DatabaseLoader(#5108)、本包SysMetadataRepository(#4867)、同文件的rethrowUnlessMetadataStoreUnprovisioned(#5532 / PR #5705)问的都是它 —— 一个驱动怪癖只教平台一遍。与 #5808「message 正则名单退休」同一类。顺带一处随之而来的修正:warn 行改用
e instanceof Error ? e.message : String(e)(catch (e: any)→catch (e: unknown))。旧版对非Error的 rejection 读e.message得到undefined,打出的正是DB hydration skipped: undefined。反向验证(方向先预测,后运行)
恢复正则版本,预测 5 红 5 绿 —— 不是整片红,红绿的分界本身就是结论:
42P01(纯 code,正则根本看不见)、errno 1146、cause链包裹,以及非 Error rejection;实测逐条一致:
那 5 例在修复后全绿。注意第 5 红(非 Error rejection)红在 message 格式化那一半、不在谓词那一半 —— 如实记,不并进「谓词证据」里充数。
事实 2:只测量、只报告,不在本 PR 实现
issue 正文自己要求先量消费方、别把两件事绑成一次改动。测量结果:
返回值
{ loaded, errors, invalid }的生产消费方只有一个 ——packages/objectql/src/plugin.ts的restoreMetadataFromDb(:1140),读法:没有任何调用方对
loaded: 0做分支处置 —— 唯一的分支是选哪条日志。于是一次读不到存储的 boot,在 kernel 自己的日志里被写成No persisted metadata found in database,且是 debug 级;协议层那条console.warn是唯一的相反信号,两条互相矛盾且不在同一日志通道上。ADR-0110 D3 的同一条规矩,落在 boot 侧。下游实际行为(
plugin.tsstart()的 Phase 2 前后,已实读):syncRegisteredSchemas按注册表建表 —— 没读到的对象自然不建;bridgeObjectsToMetadataService同理,不会桥接;restoreMetadataFromDb自身catch后return,不抛,kernel 照常 ready。代价在 Phase 2 自己的注释里已经写着(原本说的是「项目内核跳过 hydration」那一支,但对一次失败的读逐字成立):「otherwise
registry.getObject()returns nothing for them and every registry consumer (the unknown-$selectguard, hooks, relationships) silently degrades」。也就是说:存储读不到 → 一个只有 artifact 的世界被当作真相服务出去,而 kernel 报告健康。为什么不在本 PR 修:任何能表达「这次根本没读到存储」的修法都要改本方法的返回契约,并同步改
objectql/src/plugin.ts:23那个ProtocolWithDbRestore接口声明与其消费分支 —— 面比本单大,且修向有分叉(加诊断字段 / 抛出 / 只改消费方)。已在报告里列进 open questions,交分诊。必答项:对相邻单的影响
record.packageIdfrom a snake_case row — always undefined, every object overlay registers under the 'sys_metadata' sentinel at boot #4636(同方法 object 分支record.packageId恒 undefined,已转决策箱):无影响,也不冲突。本 PR 的 hunk 起于循环结束后的外层 catch;record.packageId在 :10414,相距 25 行,git 的 3 行上下文碰不到,该单选任何一个修法都不会与本改动相邻。本 PR 未触碰该行。MetadataManager.get()丢degraded判定):本 PR 对其无影响,但上面的事实 2 测量对它的定价有输入 —— 两单是同一条 ADR-0110 D3 在两层的落地,而 MetadataManager.get() 丢弃 loadDiagnosed 的 degraded 判定:loader 读不到与「这一项没声明」在 6 个消费点上不可分辨 #5840 已经证明「判定算出来了又被丢掉」在 6 个消费点上不可分辨;本单量到的是 boot 侧连算都没算(返回形状里没有这个位)。若 MetadataManager.get() 丢弃 loadDiagnosed 的 degraded 判定:loader 读不到与「这一项没声明」在 6 个消费点上不可分辨 #5840 选它的 (a) 方案(暴露诊断版读法),事实 2 顺同一个形状走会更省;若选 (b)(degraded 时抛),boot 侧照抄会让首启之外的任何读失败变成起不来,不建议跟随。/meta列表仍列出已删 overlay):无影响。那是 delete 只失效了 dispatch 视图、没失效枚举视图,与 boot 读失败的分类无关;本 PR 不动任何缓存/枚举路径。测试
新建
packages/metadata-protocol/src/protocol.load-meta-hydration-benign.test.ts(10 例,⛔ 未触碰在飞 #5619 的 13 个既有测试文件,也未触碰scripts/engine-double-contract.baseline.json):42P01/ errno 1146 /cause链)→ 均静默、{ loaded: 0, errors: 0, invalid: 0 }正常返回。这 5 种形状是从isMissingTableError已声明的签名转录的,没有另起第三份措辞清单;role "app_ro" does not exist仍告警(该谓词刻意不匹配裸does not exist);undefined;toEqual比对,记录测量、不是背书,注释写明修事实 2 时这条应当翻转、改成断言差异而非删除;loaded: 1,不打 skipped 行。假引擎只声明
find(loadMetaFromDb只调它),不声明任何写动词 —— 本包不能 import@objectstack/objectql(反向依赖成环,host-engine.ts有注释在案),因此也不手抄谓词;check:engine-double-contract按 verb 逐片扫描,成员缺席即无可 pin。命令与结果(全部前台阻塞、持容器级
flock锁):typecheck 说明(照实写,不套模板):
@objectstack/metadata-protocol没有typecheck脚本 —— 它在scripts/check-type-check-coverage.mjs里是一条已计量的 DEBT 条目,所以pnpm --filter … typecheck无从跑起。改为直接在包内跑npx tsc --noEmit做增量对照:(过程中确实抓到过一个:
new Error(msg, { cause })的 ES2022 两参重载超出本包 tsconfig 的lib,已改为Object.assign挂cause—— 谓词走的本来就是cause属性。)门禁:
check-nul-bytesOK(5701 文件)、check-engine-double-contractOK(38 pinned / 165 DEBT / 2 exempt,无新增)、check-adr-anchorsOK。合并origin/main(⛔ 未 rebase)后packages/spec有移动,已按 AGENTS §10 重建 spec 并跑check:generated—— 10 个生成物全部最新。changeset
.changeset/load-meta-hydration-benign-predicate.md,@objectstack/metadata-protocolpatch。论证「是否 user-visible」:是。运维读到的日志行两个方向都变了 —— Postgres/MySQL 首启不再打那条无从下手的
DB hydration skipped,而一个措辞恰好像「表未建」的真实故障不再被无声吞掉。日志是自托管运维唯一的 boot 期信号,所以按可见处理、带 changeset,不建议打skip-changeset。Generated by Claude Code