Repository navigation
Description
观察
#5352 / #5367 处理的是 rest-server.ts 里那串「按 message 措辞决定 HTTP 码」的正则。packages/services/service-analytics/src/analytics-service.ts 里还有第二个同类嗅探器,而且它的后果比选错状态码更重:命中即把错误换成一个空结果。
// analytics-service.ts:83
function isMissingSourceError(err: unknown): boolean {
const msg = String((err as { message?: unknown })?.message ?? err ?? '').toLowerCase();
return (
msg.includes('no such table') ||
(msg.includes('relation') && msg.includes('does not exist')) ||
msg.includes("doesn't exist") ||
msg.includes('not registered') ||
msg.includes('unknown object') ||
msg.includes('is not a registered object')
);
}
命中后 queryDataset 的 catch(analytics-service.ts:700)会 logger.warn 一句然后
return { rows: [], fields: [], totals: [] } —— 也就是 widget 画一个自信的空图,而不是报错。
这个降级本身是刻意的(对象没在本 kernel 挂载时 widget 应当显示「无数据」而非 500),#5033 已经把跨库那一支拆出来单独响亮报错。问题在于判据是措辞。
已经存在的一次命中(今天无害)
dataset-compiler.ts:260 的拒收原文:
[dataset-compiler] dataset "X" includes relationship "R" which does not exist on object "O".
它同时含 relation(在 relationship 里)和 does not exist → isMissingSourceError 返回 true。
今天不出事的唯一原因是抛点在 try 之外:queryDataset 先 const compiled = this.registerDataset(dataset)(第 626 行,compileDataset 在这里抛),而那个 try/catch 在第 697 行才开始。也就是说这是一条已经装好但没接电的雷:
- 把编译移进 try(或未来任何一处 executor / strategy 的拒收措辞里出现「relation…does not exist」),该拒收就会被吞成空结果 —— 无异常、无 4xx、无 5xx,只有一条 warn 日志和一张空图。这正是 service-analytics 的 executeRawSql 自动桥接丢弃 objectName —— dataset 原始 SQL 永远打在默认 datasource 上,凡被路由到非默认 datasource 的对象一律读成 0 #5033 立单时描述的「populated table 上 Total Spend: 0」那一类症状。
- 反方向也成立:驱动侧一条真正的「表不存在」如果换了措辞(不同驱动/不同版本),降级失效,widget 变 500。
属于 observation 类:今天没有用户会撞到,grep 也不会告诉你两条措辞正在互相纠缠。
可能的方向(不预判,留给 triage)
- A. 判据换成结构而非措辞:让「源缺失」由驱动侧带 code(例如
ERR_DATASOURCE_UNAVAILABLE已在ERROR_CODE_LEDGER里)或由isRegisteredObject探针回答,isMissingSourceError退化为兼容层并排到 code 判据之后。 - B. 最小防守:降级路径只对「不带 ADR-0112 信封的错误」生效 —— 一个已经自称 4xx 的拒收(analytics dataset 路由的 message 正则兜底没有退休时间表:六族拒收仍靠措辞分类,改一个字就换一个 HTTP 码 #5367 之后
dataset-compiler的两处就是)永远不该被降级成空结果。这一条无论 A 做不做都成立,且能直接把上面那条雷拆掉。 - C. 收紧
relation/does not exist这一支,让它只匹配 Postgres 的实际措辞(relation "x" does not exist,带引号的关系名),而不是任意同时含两个词的句子。
参考
packages/services/service-analytics/src/analytics-service.ts:83(isMissingSourceError)、:112(missingSourceRelation)、:626(编译点)、:697-:730(降级 catch)packages/services/service-analytics/src/dataset-compiler.ts:260(命中的那条措辞)- service-analytics 的 executeRawSql 自动桥接丢弃 objectName —— dataset 原始 SQL 永远打在默认 datasource 上,凡被路由到非默认 datasource 的对象一律读成 0 #5033(降级过宽导致 cross-datasource 被藏起来,已收窄)、analytics 的 filter 拒收到不了调用方:service 侧多数拒收没有 ADR-0112 信封,REST 面又用 message 正则嗅探,一律答 500 #5352 / analytics dataset 路由的 message 正则兜底没有退休时间表:六族拒收仍靠措辞分类,改一个字就换一个 HTTP 码 #5367(同一个「按措辞分类」反模式在 REST 面的两轮清理)
Activity
os-zhuang commentedon Aug 6, 2026
发现分诊(#4949 纪律):晋级,摘
finding换pm:queue,domain:services已在(落点packages/services/service-analytics/src/analytics-service.ts)。一行理由:已核 origin/main —— 嗅探器(:83)与命中措辞(
dataset-compiler.ts:262)均仍在,「装好没接电的雷」成立;方向 B(带 ADR-0112 信封自称 4xx 的拒收永不降级成空结果)是自包含、无论 A 做不做都成立的最小拆雷,#5352/#5367 同反模式清理先例在前 —— 值得现在拆,不值得等踩响。范围限 B(+C 可选收紧措辞支);方向 A(驱动侧结构化 code)涉 drivers 面,需要时独立立单。与 #5669(analyticswhere无闸门)、#5353 同包在飞/在队,认领方注意同包串行。本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
hotlong commentedon Aug 6, 2026
重定价注记(services 座位,#5716 验收后 —— 派发本单前以此为准,⛔ 不沿用立单时的成本模型):
isMissingSourceError嗅探器仍在(analytics-service.ts 的 queryDataset catch);dataset-compiler 的 "relationship … does not exist" 措辞未改,仍命中嗅探器;抛点仍在 try 之外 —— 那颗「装好没接电的雷」状态不变。Generated by Claude Code
hotlong commentedon Aug 6, 2026
认领:PM 循环第 4 轮(services 车道)
会话:
session_015a5qkLzpGXhLL2F5gvJ7dD分支:
claude/issue-5717-no-degrade-enveloped-errorsWorktree:
objectstack-issue-5717域:
domain:services文件面:
packages/services/service-analytics/src/analytics-service.ts(isMissingSourceError降级路径,现 :92/:796)+ 同包测试;⛔ 不触buildQuery/粒度判据(#6003 刚合面)、不触各拒收 throw 本体(#5963 面)、不触 drivers(方向 A 出界,分诊已限定)串行约束已清:#6003 今日刚合(同文件,行号已移),基于合并后 origin/main;同包无其它在飞(#6007 待分诊,不同函数)
执行口径:分诊晋级评论限定范围 B(+C 可选);重定价注记(13:19Z)在案 —— #5963 之后 compiler 站点已带信封,B 从今天起覆盖它。派发令随后。
Generated by Claude Code
hotlong commentedon Aug 6, 2026
验收:ACCEPT → PR #6045(实质检查全绿;两个被平台故障 cancel 的 Dogfood job 已重排,绿后转 ready 入队,本座位跟到 MERGED)。
status<500的偏离 —— 理由实测充分且与 issue 原文一致:已声明的 5xx(fail-closed 的READ_SCope_COMPILE_FAILED)被吞成空图比 400 更糟,「生产者已回答分类问题」才是 B 的本义;论证写进代码注释与 changeset,合格。missingSourceRelation同模式,「是否缺」与「缺什么」不再可能分歧。??别名读法 (#5017) #5046 教训)。isMissingSourceError仍把 Postgres 的「缺列」措辞column "c" of relation "t" does not exist判为「缺源」——文档明说不该,今天靠读路径不产生该措辞而无害 #6035(缺列措辞内含缺表短语的休眠残留)按纪律立案并在源码注释指名。经办:services 座位,会话
session_015a5qkLzpGXhLL2F5gvJ7dD(第 4 轮)。Generated by Claude Code