一句话说明
ADR-0062 D5 承诺「an optional health probe surfaced in the admin list (status)」。这个 status 从来没有实现过 —— listDatasources() 给每一条都填死 'unvalidated'。配合 checkDriversHealth() 只遍历已注册的 driver,结果是:一个连不上的 datasource,在运行时没有任何接口能告诉你它是死的。
#3816 让「有对象显式绑定」的那类连不上时拒绝启动,所以那一类现在至少炸得响。但降级放行的那两类(autoConnect: true、设了 OS_ALLOW_DRIVER_CONNECT_FAILURE)恰恰落进这个观测盲区。
事实核实
(1) status 是常量。 packages/services/service-datasource/src/datasource-admin-service.ts:127(listDatasources)与 :302:
summaries.push({
name,
// ...
status: 'unvalidated', // ← 无条件
});
而契约 contracts/datasource-admin-service.ts:68-82 声明的是三态、并注释成 "current health":
/** A datasource with its provenance and current health (no secrets). */
export interface DatasourceSummary {
// ...
/** Validation health: `unvalidated` until the first validate/test runs. */
status: 'ok' | 'error' | 'unvalidated';
}
全仓库没有任何地方把它写成 'ok' 或 'error'。声明三态、只发一态 —— Prime Directive #10 点名的 declared ≠ enforced 形状,这次发生在返回值而不是 switch 上。
(2) health 探针看不见它。 packages/objectql/src/engine.ts:1984 的 checkDriversHealth()(#3756)遍历的是 this.drivers:
return Promise.all(
Array.from(this.drivers, async ([driverName, driver]) => { /* ... */ }),
);
一个 connect 失败的 datasource 从未被 registerDriver,所以它不在 this.drivers 里 —— 探针不会报它 unhealthy,而是压根不提它。readiness probe 因此看到一片绿。
影响
一个 autoConnect: true 的 datasource 连不上(或者操作者设了 OS_ALLOW_DRIVER_CONNECT_FAILURE 让启动过去):
- Setup → Datasources 里它显示
unvalidated —— 与一个从没被测试过但完全健康的 datasource 一模一样,两种状态在 UI 上无法区分。
GET /api/v1/datasources 同样。
- readiness 探针不报它。
- 唯一的痕迹是启动时滚过去的一行 warn / 一次
DEGRADED BOOT banner —— 进程活得越久,这条线索越不可能还在任何人的屏幕上。
也就是说:排查手段是重启服务器再看日志。
建议处置
分两半,可以分开做:
A. status 说真话。 DatasourceConnectionService 已经为每次 connect 返回 ConnectResult { status, reason }(七种:connected / already-registered / skipped-policy / skipped-no-infra / skipped-unsupported / failed-credentials / failed-degraded),这些结果当场被丢弃。让服务留存最近一次结果,listDatasources() 读它。现有三态不够表达 —— skipped-policy(被拒)和 failed-degraded(连不上)在运维上是两回事,建议顺带扩 DatasourceSummary.status 并带上 reason。注意这是公开契约,要走 spec 变更而不是就地加字段。
B. 探针覆盖「应该连上但没连上」。 checkDriversHealth() 的语义是「已注册的 driver 现在能不能服务」,它没错;缺的是「有哪些 datasource 本该注册却没有」。倾向不要去改 checkDriversHealth 的遍历源(那会把两个问题混在一个返回值里),而是让 datasource 侧单独暴露未连接集合,由 readiness 端点合并。
需要先定的:一个 autoConnect datasource 没连上,readiness 该不该判 not-ready? 倾向不该 —— autoConnect 的语义就是「能连就连」,没有对象声明依赖它,判 not-ready 会把一个可选副本变成整机下线开关。但它必须可见。这条如果定反了会很难回退,值得先决定再动手。
关联
一句话说明
ADR-0062 D5 承诺「an optional health probe surfaced in the admin list (
status)」。这个status从来没有实现过 ——listDatasources()给每一条都填死'unvalidated'。配合checkDriversHealth()只遍历已注册的 driver,结果是:一个连不上的 datasource,在运行时没有任何接口能告诉你它是死的。#3816 让「有对象显式绑定」的那类连不上时拒绝启动,所以那一类现在至少炸得响。但降级放行的那两类(
autoConnect: true、设了OS_ALLOW_DRIVER_CONNECT_FAILURE)恰恰落进这个观测盲区。事实核实
(1)
status是常量。packages/services/service-datasource/src/datasource-admin-service.ts:127(listDatasources)与:302:而契约
contracts/datasource-admin-service.ts:68-82声明的是三态、并注释成 "current health":全仓库没有任何地方把它写成
'ok'或'error'。声明三态、只发一态 —— Prime Directive #10 点名的declared ≠ enforced形状,这次发生在返回值而不是switch上。(2) health 探针看不见它。
packages/objectql/src/engine.ts:1984的checkDriversHealth()(#3756)遍历的是this.drivers:一个 connect 失败的 datasource 从未被
registerDriver,所以它不在this.drivers里 —— 探针不会报它 unhealthy,而是压根不提它。readiness probe 因此看到一片绿。影响
一个
autoConnect: true的 datasource 连不上(或者操作者设了OS_ALLOW_DRIVER_CONNECT_FAILURE让启动过去):unvalidated—— 与一个从没被测试过但完全健康的 datasource 一模一样,两种状态在 UI 上无法区分。GET /api/v1/datasources同样。DEGRADED BOOTbanner —— 进程活得越久,这条线索越不可能还在任何人的屏幕上。也就是说:排查手段是重启服务器再看日志。
建议处置
分两半,可以分开做:
A.
status说真话。DatasourceConnectionService已经为每次 connect 返回ConnectResult { status, reason }(七种:connected/already-registered/skipped-policy/skipped-no-infra/skipped-unsupported/failed-credentials/failed-degraded),这些结果当场被丢弃。让服务留存最近一次结果,listDatasources()读它。现有三态不够表达 ——skipped-policy(被拒)和failed-degraded(连不上)在运维上是两回事,建议顺带扩DatasourceSummary.status并带上reason。注意这是公开契约,要走 spec 变更而不是就地加字段。B. 探针覆盖「应该连上但没连上」。
checkDriversHealth()的语义是「已注册的 driver 现在能不能服务」,它没错;缺的是「有哪些 datasource 本该注册却没有」。倾向不要去改checkDriversHealth的遍历源(那会把两个问题混在一个返回值里),而是让 datasource 侧单独暴露未连接集合,由 readiness 端点合并。需要先定的:一个
autoConnectdatasource 没连上,readiness 该不该判 not-ready? 倾向不该 ——autoConnect的语义就是「能连就连」,没有对象声明依赖它,判 not-ready 会把一个可选副本变成整机下线开关。但它必须可见。这条如果定反了会很难回退,值得先决定再动手。关联
checkDriversHealth)、显式绑定的 datasource 连不上只降级成一条 warning:绑上去的对象在启动后全废,直到查询时才炸 #3758 / fix(datasource): an explicitly-bound datasource that cannot connect refuses the boot (#3758) #3816(启动期判决)