Skip to content

悬空引用巡检:行预算耗尽后剩余对象被静默丢弃 —— 报告里没有任何桶说「这些对象没被看过」 #5718

Closed

Description

@os-zhuang

发现自 #4743 事实二的实现过程(PR 见下方引用)。未认领。

现象

packages/objectql/src/integrity/dangling-reference-audit.ts 的主循环:

objects: for (const { obj, refFields } of prioritise(all)) {
    if (report.scanned >= maxRows) break;

maxRows(默认 DEFAULT_MAX_ROWS = 5_000)一旦耗尽,循环直接 break,其后所有对象既没被读、也没被记录到任何桶里。报告返回的 dangling: [] / provenance: [] 对这些对象而言不是「查过了、干净」,而是「压根没看」——但报告里没有任何字段区分这两者。

为什么这是缺陷,而不是「预算本来就是抽样」

这个文件自己的设计立场恰恰是反过来的。它为「不完整」造了三个专门的桶,每一个都是为了让沉默无法被读成清白:

注释原话:「Together they are what stops 0 dangling from ever being read as everything is fine」。而对象级的预算耗尽是唯一一条没有对应桶的不完整路径:truncatedObjects 覆盖的是「一个对象内部读不全」,覆盖不了「这个对象整个没轮到」。

#4743 让它更容易被踩到(但不是它造成的)

原先只有「带非 readonly 引用字段」的对象才会被读,大量表因为 refFields.length === 0 直接跳过、不耗预算。#4743 把 applySystemFields 注入的溯源族(created_by / updated_by / organization_id)纳入巡检后,几乎每个对象都有可审计字段,预算消耗显著上升,>= maxRows 提前触发的概率随之上升。

PR 里已经做了力所能及的缓解——prioritise 加了第三档(安全面 → 带业务引用的对象 → 仅溯源的对象),保证有限预算优先花在业务发现上。但那只改变了谁先被读,没有解决没轮到的对象不被记录。这两件事是独立的,所以分开填。

建议方向(留给 triage,不预设)

新增一个诚实桶,例如 unscannedObjects: string[](或 unscanned: number,若怕对象多时数组过大),在 break 之前把剩余对象名记下来。命名与形状按该文件既有分组风格推导。

边界

Refs #4551(桶的设计立场)、#4747(aborted 是同一族的第三个桶)、#4743(把这条路径的触发概率抬高的变更)。

Blocked-by: #4743

(分诊座位补 2026-08-06:同文件在 #4743 在飞面上——prioritise 第三档尚未见于 origin/main 的 integrity 目录——#4743 落地后本单回队。)

Activity

  1. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    ContributorAuthor

    分诊:入队 pm:queue + domain:engine-core,同时挂 pm:blocked,正文已补 Blocked-by: #4743。

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


    Generated by Claude Code

  2. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    on Aug 6, 2026
  3. self-assigned this
    on Aug 6, 2026
  4. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    ContributorAuthor

    解锁 + 认领:PM 循环第 3 轮(engine-core 车道)。Blocked-by: #4743 的记录理由是「其 PR 改同一文件、prioritise 三档未上 main」—— 该 PR(#5719,事实二)已合并、三档已在 origin/main,阻塞实质已满足;#4743 issue 本身仍开放只因事实一(engine.ts 注释,另一文件),不构成本单阻塞。按 unlock sweep 摘 pm:blocked 并注明理由回队认领。
    会话:session_01V7WetGmnfoXNn8cLieKKmx
    分支:claude/issue-5718-audit-unscanned-bucket
    Worktree:objectstack-issue-5718
    域:domain:engine-core
    文件面:packages/objectql/src/integrity/dangling-reference-audit.ts(主循环 + 报告类型)、其测试、.changeset/*.md。⛔ 不碰 engine.ts / lifecycle-service.ts(在飞)。
    方向照 issue:新增诚实桶(如 unscannedObjects),break 前记下未轮到的对象;命名/形状/warn 语义按该文件既有分组推导(最近亲缘是 truncatedObjects —— 同为预算类不完整);新键 optional(消费方字面量替身约束,同 #5719 的先例);认领方先读 #5719 的 diff 对齐三档 prioritise 现状。


    Generated by Claude Code

  5. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    ContributorAuthor

    PM 评审(会话 session_01V7WetGmnfoXNn8cLieKKmx):PR #5760 ACCEPT,待 CI 绿后放行。

    已读全量 diff。要点:

    1. 桶的形状对:unscannedObjects optional 类型 + 运行时恒赋值(Required<Pick<...>> 本地视图),与 aborted/哨兵拆掉之后:#4441 的 readonly 收窄与 #4551 的巡检跳过还需要吗?——注释已过期,跳过范围值得收窄 #4743 provenance 同契约;按 prioritise 扫描顺序记名,可直接回喂 options.objects 续跑。
    2. 边界处理是本单的含金量所在:三处停点语义各不相同且各有测试钉住——预算停/对象前叫停从 i 起记,probe 中途叫停从 i+1 起记(该表已开、部分已查,记「未轮到」是另一种谎),枚举注册表前叫停由 aborted 独自承担(无名单可给)。「无引用字段」与「调用方排除」两类不入桶是结构保证(prioritise 前置丢弃 / only 收窄)而非特判。
    3. 告警判决正确:不单独抬线(大库每次健康运行都会耗预算,常亮告警 = 每个 os migrate 子命令关停时,悬空引用巡检都会把 sys_metadata / sys_view_definition 报成 unreadableObjects(连接已关闭) #4747 的坏警报),逐项入载荷。
    4. 反向验证方法论合格:两处限位分别扰动、方向先判后跑,且明确指出「只断言 dangling/scanned 的测试在旧静默 break 下同样绿」——名字才是被测行为。
    5. 纪律:文件面严守(未碰 engine.ts / lifecycle-service.ts);changeset patch;先读了 feat(objectql): 悬空引用巡检纳入 readonly 溯源族,单独分桶为 provenance (#4743 事实二) #5719 diff 对齐三档现状(正文中 prioritise 关系一段可证)。

    CI 绿后我会转 ready 并挂 auto-merge。


    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

pm:dispatchedDispatched to a dev agent by /pm-dispatch

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions