Skip to content

FS 监听改动同样不失效本节点的 listCache —— handleFileEvent 只通知 watcher(#5109 的本地同形缺陷) #5218

Closed

Description

@os-zhuang

发现于 #5109(集群对端广播不失效本节点缓存)实现期,不在该单范围内,故单独立卡。未认领 —— 只是记录。

现象

packages/metadata/src/node-metadata-manager.ts 的 handleFileEvent()(:88-131)在 chokidar 报告 add / change / unlink 之后,只做两件事:重新 load() 一次文件内容,然后 notifyWatchers(type, event)。

    let data: any = undefined;
    if (eventType !== 'deleted') {
      data = await this.load(type, name, { useCache: false });   // 只读,不写任何缓存
    }
    // ...
    this.notifyWatchers(type, event);                            // 只通知

它不碰 this.listCache,也不碰 this.registry —— 而 load() / loadDiagnosed()(metadata-manager.ts)是纯读路径,同样不写这两个缓存(registry 只由 register / registerInMemory / 回滚三处写入)。

这与 #5109 是同一形状、不同触发源:本节点自己的 register() / unregister() 会 invalidateListCache(type) 再通知;文件系统改动只通知、不失效。

后果

rootDir 下手改一个 view/<name>.json(或任何被监听的元数据文件)之后:

另外 type === 'api' 时,EndpointMatcher 索引也只经 watcher 那条缝失效(#5089 装的 subscribe('api', …) 覆盖了它),invalidateListCache 这条缝在此路径上是空的 —— 目前不构成额外缺陷,但两条缝的覆盖面在这里是不对称的,值得一并确认。

触发前提(请在裁决时核实)

FS 监听要开着才会命中。MetadataPlugin 的默认是 watch: true,仅在 bootstrap: 'artifact-only' 下被强制关掉(packages/metadata/src/plugin.ts :241-249);packages/runtime/src/standalone-stack.ts :341 显式传 watch: false。也就是说:artifact 模式的 os dev 与 standalone 不受影响,非 artifact 的默认 MetadataPlugin 装配受影响。命中面主要是开发期,不是生产多节点。

修法(一行,与 #5109 同一样板)

notifyWatchers 之前补 invalidateListCache(type)。invalidateListCache 目前是 private,NodeMetadataManager 是子类,需要放宽到 protected(或复用 #5109 落地的 invalidateForForeignWrite(type, name) —— FS 改动同样是「不是本进程写的」那一类,语义正好对上:删而不预填,穿透回 loader)。

registry 要不要一并删,这里比 #5109 更简单:FS 加载的条目本来就不进 registry,只有当同名条目此前被 register() / registerInMemory() 写过时才存在,而那种情况下删除同样是对的(穿透回 loader 就是文件的真相)。

复现

// rootDir/view/v_a.json 存在
const mgr = new NodeMetadataManager({ rootDir, watch: true, formats: ['json'] });
await mgr.list('view');                 // 预热,返回 [v_a]
// 此时在磁盘上写入 rootDir/view/v_b.json,等 chokidar 报告 add
await mgr.list('view');                 // 仍然是 [v_a],最长 30s
await mgr.get('view', 'v_b');           // 却能读到 —— 两个读接口彼此矛盾

关联

#5109(集群那条同形缺陷,已修,invalidateForForeignWrite 即本单可复用的样板)、#5184(同一个 listCache 字段的第三条缺陷:降级结果照常入缓存)、packages/metadata/src/node-metadata-manager.ts handleFileEvent()、packages/metadata/src/metadata-manager.ts invalidateListCache()。

三单共用 listCache 这一个字段,建议串行。

Activity

  1. added
    bugSomething isn't working
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    on Aug 4, 2026
  2. os-zhuang commented on Aug 4, 2026

    @os-zhuang
    ContributorAuthor

    分诊(PM 循环第 2 轮,会话 session_01Pbu27iNUfQCHeuS551Rqo7):落点 packages/metadata(node-metadata-manager.ts + metadata-manager.ts 的可见性放宽)→ domain:engine;挂 pm:blocked,Blocked-by: #5219 —— 修法样板 invalidateForForeignWrite 随该 PR 落地,且与其同文件,合并前不派。#5219 合入后转 pm:queue 即派;届时与 #5184(同一 listCache 字段的第三条缺陷)保持串行,正文「三单共用一个字段,建议串行」的约束进派发词。


    Generated by Claude Code

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

    @os-zhuang
    ContributorAuthor

    认领:PM 循环第 2 轮(阻塞解除:#5219 已合入 main,533a0a4,invalidateForForeignWrite 样板可用)
    会话:session_01Pbu27iNUfQCHeuS551Rqo7
    分支:claude/issue-5218-fs-watch-invalidate-list-cache
    Worktree:objectstack-issue-5218
    域:domain:engine
    文件面:packages/metadata/src/node-metadata-manager.ts(handleFileEvent)、packages/metadata/src/metadata-manager.ts(仅方法可见性放宽 / 复用所需的最小触碰)、本包测试、changeset(越界即停,报告说明)

    串行约束:#5184(同一 listCache 字段的写入路径判据)排在本单之后,本单 ⛔ 不碰 cacheListResult() 与缓存条目形状。⛔ packages/spec/** 零改动。在飞不相交已核:#5038(objectql/service-automation)、#5134(driver-sql)与本面不交。


    Generated by Claude Code

  6. os-zhuang commented on Aug 4, 2026

    @os-zhuang
    ContributorAuthor

    验收:ACCEPT —— PR #5229(等 CI 绿后入队)

    会话:session_01Pbu27iNUfQCHeuS551Rqo7(PM 循环第 2 轮)

    落地内容:handleFileEvent() 在 notifyWatchers 之前调 invalidateForForeignWrite(type, name)(复用 #5219 样板,private→protected,零新失效逻辑)。dev 没有假设复用成立而是逐个核了 registerInMemory 调用方(单名删除契约保护代码态构件),并证明只放宽 invalidateListCache 是不够的(register() 写过的条目在 get()/list() 里遮蔽 loader —— 有专门用例);issue 请核的 EndpointMatcher 不对称确认非缺陷,本单把第二条缝接对称,幂等有实测。

    实况核验:draft ✓ / Fixes 行 ✓ / 4 文件全在面内 ✓ / ⛔ cacheListResult 零触碰(#5184 的面)、spec 零改动 ✓ / 11 用例中 9 个在回退修复后失败(2 个幸存者是负向对照与 api guard,注释如实标明)/ 真 chokidar 端到端用例含 ready 竞态修复,3 连跑稳定 / 全包 444 绿 / tsc --noEmit 基线 92→92 零增量。

    顺带发现处置:#5228(catch 不可达,坏文件广播为 data: null)已按纪律立卡带 finding,定级论证完整(当前无消费者读 event.data,休眠),留发现分诊轮;其建议方向 A(改用 loadDiagnosed 按 degraded 分流)先记录在案。

    → #5184(listCache 写入路径判据,PM 已裁方向 1)在本单合入后即为下一单。


    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

bugSomething isn't workingpm:dispatchedDispatched to a dev agent by /pm-dispatchpm:queueReady for the PM dispatch loop

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions