Skip to content

ADR-0057 D10 的 dashboard requiresService 组件门禁在 GET /meta/:type/:name 的缓存分支(默认路径)被完全跳过 #5881

Description

@baozhoutao

发现于 #5563 实施(信封收敛)期间的旁证测量,与该单的改动无关 —— 改前改后同样成立。基线 origin/main @ 55c74deeb。

事实

packages/rest/src/rest-server.ts 的单条元数据读取 handler 里,ADR-0057 D10 的 dashboard 组件门禁(filterDashboardForUser,按 widget.requiresService 剔除指向未注册可选服务的磁贴)只写在非缓存分支里:

} else {
    // 非缓存分支
    const envelope = await p.getMetaItem({...});
    ...
    if (RestServer.metaTypeSingular(req.params.type) === 'dashboard' && visible) {
        ... visible = this.filterDashboardForUser(visible, serviceGate);
    }

而进入缓存分支的条件是:

if (metadata.enableCache && p.getMetaItemCached && !isAppType && !isDraftRead
    && !previewDrafts && !packageScoped && req.params.type !== 'doc' && req.params.type !== 'book')

app 被 !isAppType 显式排除在缓存之外(注释写明:正是为了让 per-user RBAC 过滤能跑),doc / book 也被排除(§6.7 audience 是 per-caller)。dashboard 没有被排除。而 enableCache 默认为 true(packages/spec/src/api/rest-server.zod.ts 的 enableCache: z.boolean().default(true))。

于是:默认配置下,一次 GET /api/v1/meta/dashboard/:name 走缓存分支,filterDashboardForUser 一次都不会执行。

实测

在 worktree 里对着真 RestServer 跑一次探针(protocol 同时提供 getMetaItem 与 getMetaItemCached,serviceExistsProvider 报告 org-scoping 未注册,dashboard 有两个组件,其中 w_orgs 声明 requiresService: 'org-scoping'),捕获 res.json 的实参:

cachedCalls:   1
widgetsServed: [ "w_users", "w_orgs" ]

w_orgs 被原样送出。把同一请求切到非缓存分支(metadata.enableCache: false),门禁生效、w_orgs 被剔除 —— 这条断言现在钉在 packages/rest/src/rest.test.ts 的 filterDashboardForUser 一组里(#5563 PR 内),该用例显式关掉缓存才跑得出绿,注释里也记了这条为什么。

(探针脚本是一次性的,未提交。)

影响

ADR-0057 D10 的立论是「服务端是权威的可见性门禁」—— 客户端的隐藏只是礼貌。这里默认配置下门禁根本不运行,所以:

  • 在没有该可选服务的部署里,console 会渲染一块绑定到缺失服务的死磁贴(D10 原文点名的场景:单租户运行时里的 Organizations KPI);
  • 与 app 的处置不对称:app 为了让 per-user 过滤能跑而特意绕开缓存,dashboard 的门禁却写在只有绕开缓存才会到达的分支里 —— 两处是同一天同一个 ADR 的两半;
  • 属于「声明了却没有强制」:门禁代码在、测试在(测试直接调私有方法或关掉缓存,因此一直绿),没有任何 gate 会发现默认路径上它不执行。

可能的处置(未定,交分诊)

  • A:把 dashboard 加进缓存排除条件(与 app 同款),代价是 dashboard 读失去 ETag 快路径;
  • B:门禁提到分支之外,缓存与非缓存两条路径出口处统一跑一次(需要注意 ETag 是 per-published-checksum 而门禁是 per-deployment 的,不是 per-caller,所以共享 ETag 未必不安全);
  • B 看起来更贴合「一次读取一个出口」,但 ETag/缓存键与门禁结果的关系需要确认,不代拍。

关联:#5563(在其实施中测得,两者互不阻塞)、ADR-0057 D10。

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions