Skip to content

[metadata-protocol] SysMetadataRepository 的 nextEventSeq()/nextItemVersion() 同样把读失败当「表还没建」,静默从 1 重新发号 —— #4825 在 canonical 路径上的同形缺陷 #4867

Description

@os-zhuang

发现于 #4825(修 DatabaseLoader.nextEventSeq())。仅记录,未在该 PR 中修改 —— 落点在另一个包(packages/metadata-protocol),按 Prime Directive #10 单开。

现象

packages/metadata-protocol/src/sys-metadata-repository.ts 有两个同形的 catch

// nextEventSeq(ctx) — 约 1051 行
    return max + 1;
  } catch {
    // Table not provisioned yet (fresh DB) — start at 1.
    return 1;
  }
// nextItemVersion(ref, ctx) — 紧随其后
    return max + 1;
  } catch {
    return 1;
  }

两者都读 sys_metadata_history,都把全部读失败折成 return 1

为什么值得单开(比 #4825 更该修,不是更不该)

#4825 修的是 DatabaseLoader —— TSDoc 自称 legacy、非事务的那条路径。这里是 canonical 路径#4825 的正文与分诊都把 SysMetadataRepository 称作「历史写入应当收敛过去的地方」。同一个缺陷在目的地上原样存在。

而且这里有两个数字,不是一个:

  • event_seq —— 历史排序 / 回滚定位的依据。已有 N 行时一次瞬时读失败让下一条拿到 1,与既有行撞号;
  • version —— nextItemVersion() 的 TSDoc 明说它刻意从 history 取 MAX「so delete + recreate continues incrementing instead of restarting at 1」。一次读失败正好把它恢复成它明确要避免的那个行为:lineage 从 1 重启,与既有 lineage 行撞号。而 MetadataManager.rollback(type, name, version)POST /api/v1/meta/:type/:name/rollback 正是按 version 定位快照的 —— 撞号之后回滚可能指向另一条记录的同号版本。

关键危害与 #4825 相同,是「落盘的字节是错的」而不是「字节没落盘」:insert 成功、日志一行没有、系统对外完全正常,错误只在版本顺序里,重试不修、重启也不修。

有一点值得注意但不改变结论:这两个调用都在事务里(put/delete 的 txn body)。事务解决的是并发撞号,解决不了读失败被折成 1——一个成功提交的事务照样可以提交一个错号。

建议

#4825 落地的方案一致,直接复用它的判别器,不要另起一套:

⚠️ 跨包复用需要先定一件事:该判别器目前是 @objectstack/metadata内部工具(未从包入口导出),而 @objectstack/metadata-protocol 是另一个包。三个选项,值得在动手前定:

  1. @objectstack/metadata 导出(最小改动,但把一个内部工具变成公共 API 面);
  2. 下沉到两个包共同的依赖里(@objectstack/types@objectstack/spec/shared)—— 注意 Prime Directive ✨ Set up Copilot instructions #2packages/spec 不放业务逻辑,错误分类算不算,需要判断;
  3. metadata-protocol 里复制一份 —— 不建议,正是 [metadata] nextEventSeq() 把驱动读失败也当成「表还没建」,静默从 1 重新发号 —— #4632 同形,机械检查覆盖不到 #4825 刻意避免的「同一问题两套判别」。

倾向 2(若落点合法)或 1,由维护者定。

参考

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions