Skip to content

/ready 探针看不见数据库:driver 运行期掉线,k8s 不摘流量也不重启,而每个请求 500 #3756

Description

@os-zhuang

一句话说明

/health 恒返回 ok/ready 只看 kernel state 是不是 running —— 两者都从不碰 data driver。数据库在启动之后掉线,探针照样绿,负载均衡不摘这个副本、编排器不重启它,而它服务的每个请求都 500。

#3741 / #3751 修掉了这个问题的启动期版本(连不上就拒绝启动)。运行期版本还在。

事实核实

packages/runtime/src/http-dispatcher.ts:283-296 —— /health

this.domainRegistry.register({
  prefix: '/health', match: 'exact', methods: ['GET'],
  handler: async () => ({
    handled: true,
    response: this.success({
      status: 'ok',
      timestamp: new Date().toISOString(),
      version: '1.0.0',
      uptime: typeof process !== 'undefined' ? process.uptime() : undefined,
    }),
  }),
});

同文件 :302-311 —— /ready

handler: async () => {
  const state: string = typeof (this.kernel as any)?.getState === 'function'
    ? (this.kernel as any).getState()
    : 'running';
  return state === 'running'
    ? { handled: true, response: this.success({ status: 'ready', state }) }
    : { handled: true, response: this.error('Service not ready', 503, { state }) };
},

三点:

  1. 两个探针都没有任何数据层依赖。 /readystate === 'running' 在 kernel bootstrap 完成的那一刻置位,此后除非进入 stopping/stopped 否则永不改变 —— 它回答的是"kernel 生命周期到哪一步了",不是"这个副本现在能不能服务请求"。
  2. driver 的 checkHealth() 早就存在且很便宜driver-sqlSELECT 1driver-mongodbdb.command({ping:1})),但只被 datasource-admintestConnection 消费(datasource-admin-plugin.ts:466)。没有任何探针路径调用它。
  3. ObjectQL 引擎根本没有健康检查面。 driversprivate drivers = new Map<...>()engine.ts:326),没有 getter,没有聚合的 health 方法 —— 就算探针想问也没地方问。

PluginHealthMonitorpackages/core/src/health-monitor.ts)管的是 plugin,不覆盖 driver 连接。

影响

数据库在进程启动后不可用(重启、failover、网络策略变更、连接池耗尽、凭据轮换):

这跟 #3741 是同一个病的另一面:声称在报告健康,实际没检查那个唯一重要的依赖。

建议处置

关键是不要把两个探针混为一谈 —— 它们的正确答案是相反的:

/health 保持不依赖数据库,并把这一点写进注释。 liveness 探测失败 → 编排器重启 pod。重启一个 pod 不会修好宕掉的数据库,只会让所有副本在数据库抖动期间陷入重启风暴,同时销毁掉本可以正常服务的 in-flight 请求。liveness 只该回答"这个进程还活着吗"。现状是对的,缺的是说明它为什么对,否则下一个人会"顺手"把 DB 检查加进去。

/ready 检查 data driver。 readiness 探测失败 → 把副本摘出 LB 轮转,不重启。一个连不上数据库的副本 100% 的请求都会失败,摘掉它正是 readiness 存在的意义。具体:

  1. ObjectQL 加一个公开的聚合健康检查(drivers 目前是 private,外部无从问起)。
  2. /ready 特性探测地消费它 —— 没有 data engine / 没有该方法(lite kernel、edge)就退回现有行为,不能因此把这些运行时判成 not-ready。
  3. 必须带超时。 checkHealth() 本身吞异常返回 false,但在一个死掉的连接池上,knex 的 SELECT 1 会一直等到 acquireConnectionTimeout(默认 60s)。一个挂住的探针和一个撒谎的探针一样糟 —— 超时即判 not-ready。
  4. 必须有短 TTL 缓存。 k8s 默认几秒一次探测 × 每个副本,不能每次都打一个 DB 往返。1 秒左右的 memo 就够,且不会让判定明显滞后。

关联

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions