Skip to content

ObjectQL seedAutonumber 把读故障答成 return 0 —— 对已有 N 行的表重新从 1 发号,自增号与既有行相撞且零日志(#4825 同族,活体) #5979

Closed

Description

@hotlong

发现于 #5186(为「读接缝把故障答成空值」这一族新增闸门)实施期,由新规则在 packages/objectql 扫出;不在该单文件面内(#5186 是纯 scripts/ 闸门单,不改被扫包),故单独立卡并已作为 baseline 条目记录在 scripts/durability-read-invention.baseline.json。

现象

packages/objectql/src/engine.ts 的 seedAutonumber()(约 2051–2087 行,以 origin/main 为准):

private async seedAutonumber(object, field, prefix, execCtx?): Promise< number > {
  try {
    const rows = await this.find(object, { fields: ['id', field], limit: 5000, context: execCtx } as any);
    let max = 0;
    // … 从已有值里取出最大计数 …
    return max;
  } catch {
    return 0;
  }
}

catch 里:没有任何日志、没有 rethrow、没有按错误类型区分,直接编了一个 0 出来。

为什么这是 #4825 那一半(写进去的数是错的)

这是「读接缝把故障答成空值」家族里最贵的形状,和 #4825 的 nextEventSeq 的 catch { return 1 } 完全同构:

  • 表里已经有 N 行、自增号已经发到 PRE-000123;
  • 一次连接抖动 / 超时 / 权限失败让这次 find 抛出;
  • catch 答 0,计数器从头开始,下一批自增号与已存在的行相撞;
  • 插入成功,一行日志都没有,success 与行计数全部读起来正常。

和「字节没落盘」不同,这是落盘的值是错的——重试不修,重启不修。

值得一提的是:这个风险 issue 代码里已经写着。同一个 try 块上方的注释(#4371 的修复注释)原文就说:

the catch below would have swallowed the guard's rejection into "seed from 0", i.e. duplicate autonumbers.

即:读那半边在 #4371 修好了,catch 这半边留在原地。

修法(与 #4825 / #5108 同一处方)

按错误类型区分,只在良性分支返回零值:

import { isMissingTableError } from '@objectstack/metadata/errors';

} catch (error) {
  // 良性且仅良性:表尚未 provision,确实没有行,从 0 起号不会撞任何东西
  if (isMissingTableError(error)) return 0;
  throw error;   // 其余一律上抛:调用方不得用没读到的数据推算号段
}

⚠️ 需要确认的落点问题(实现时判断,不在本卡预判):@objectstack/objectql 依赖 @objectstack/metadata 是否成立/是否会成环。若不成立,packages/metadata/src/errors.ts 的头注释已经把三条路线写清楚了(其中方案 2「沉到公共依赖 @objectstack/types / @objectstack/spec/shared」当时只是被判为「那一轮 out of scope」,并未被否决)——这正是需要该方案的那个调用方。

验收

  • seedAutonumber 的 catch 只在 isMissingTableError 为真时返回 0,其余上抛;
  • 有测试钉住:表未建 → 从 0 起号且不抛;驱动报连接错误 → 上抛,不发号;
  • 修好后删除 scripts/durability-read-invention.baseline.json 里的 packages/objectql/src/engine.ts::seedAutonumber 条目(该 baseline 是 shrink-only,条目失效即红)。

关联

#5186(新增本族闸门,本卡由它扫出)、#4825(同族、同形状,history event_seq)、#5108(同族,DatabaseLoader 五处读)、#4728(同族起点,DDL 侧)、#4632(规则)、#4371(同一个 try 里读那半边的修复,其注释预言了本卡)、AGENTS.md「Degradation log levels」「Absence must be loud」。

Activity

  1. claude commented on Aug 6, 2026

    @claude
    Contributor

    分诊:pm:queue(可派发缺陷,本批最高严重度)+ 维持 domain:engine-core。

    ⚠️ 与 #5980 同属本轮扫到的**「半分诊」态**:有 bug + domain:engine-core 但无 pm-state 标签,对所有队列视图不可见。

    过时前提检查(origin/main 9e3709a)—— 逐行实读,全部成立

    • packages/objectql/src/engine.ts:2046 seedAutonumber() 在,结尾 } catch { return 0; } 原样未改(实读 :2085-2087);
    • 调用点 :2032 if (next == null) next = await this.seedAutonumber(...) 在 ⇒ 这条路径是活的,不是死代码;
    • 正文引用的 [P2] A direct engine call silently drops sort/select/skip/populate — declared query contract, zero enforcement #4371 注释仍在同一个 try 上方(实读 :2057 区段的 "the catch below would have swallowed the guard's rejection into 'seed from 0'")—— ⚠️ 代码里已经写着这个风险,这一条把本单从「推断」升为「仓内既有书面预言」;
    • baseline 条目 packages/objectql/src/engine.ts::seedAutonumber 仍在(durability-read-invention.baseline.json:49),shrink-only ⇒ 验收判据机械。

    为什么入队(且建议优先于 #5980):这是「读接缝把故障答成空值」家族里唯一会把错值落盘的一支 —— 其余同族(#5108 / #5532 / #5980)的后果是「答案是空的」,可由重试自愈;本单的后果是自增号从 1 重发、与既有行相撞并插入成功,零日志、success 与行计数读起来全部正常。重试不修、重启不修。落点/修法/验收三项齐备,无产品裁决成分。

    域锚定:落点 packages/objectql/src/engine.ts ⇒ domain:engine-core。

    ⚠️ 派发时必须传下去的一个未决实现问题(正文已点名,分诊不代拍):处方需要 isMissingTableError,而 @objectstack/objectql 依赖 @objectstack/metadata 是否成环需实测。packages/metadata/src/errors.ts 头注释已列三条路线,其中「沉到公共依赖」当时只是被判为 out of scope、并未否决。这是实现期的技术判断(dev 可解),不是维护者决策 ⇒ 不因此转决策箱。

    串行约束(硬,派发时必读):packages/objectql/src/engine.ts 本轮是全仓最热的文件之一 ——

    查重:三仓 open issue / PR 各搜一遍(seedAutonumber / autonumber 相撞 / catch-return-0)—— 同族 #4825(nextEventSeq 的 catch { return 1 },同构)、#5108、#4728 均已收官;本单是活体实例,入口唯一,objectui / cloud 无影子单。

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  2. self-assigned this
    on Aug 7, 2026
  3. baozhoutao commented on Aug 7, 2026

    @baozhoutao
    Contributor

    认领(执行席 PM,engine-core 车道,第 8 轮)

    • 会话:session_019Q7oc7ASjh8yxyS3Yz78We
    • 分支:claude/issue-5979-seed-autonumber-outage
    • 工作树:../objectstack-issue-5979(worktree-first)
    • 文件面:packages/objectql/src/engine.ts(仅 seedAutonumber 的 catch)+ 新增测试 + scripts/durability-read-invention.baseline.json 摘条目;isMissingTableError 引入路线按 packages/metadata/src/errors.ts 头注释三路线实测决定,可能触 packages/objectql/package.json 或公共依赖包

    遵守分诊串行约束:PR #6013(同文件)已 MERGED,开工即以 origin/main 为基;⛔ #5929(同文件 delete() 门)不同轮、本单不触其区域。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions