Skip to content

被 connect policy 拒绝的 datasource,查询时报的错说不出原因:getDriver 只会说 "is not registered" #3828

Description

@os-zhuang

一句话说明

DatasourceConnectPolicy 拒绝一个 datasource 后,它停在 metadata-only(这是对的 —— 见 #3816 里 ADR-0062 D5 的修订说明:拒绝是「决定」,不是「失败」,不该 fail-fast)。但引擎不知道发生过拒绝,所以绑定到它的对象在查询时抛的是一句无差别的 Datasource 'x' is not registered. —— 跟真实原因(「你的套餐/egress 策略不允许连接外部数据源」)相距很远。

这是 #3758 那个形状的残余:那次修的是「静默启动」,这次剩下的是「错得不知所云」。

事实核实

拒绝路径。 packages/services/service-datasource/src/datasource-connection-service.ts:251:

if (!decision.allow) {
  this.logger?.info?.(`datasource '${name}': connect denied by policy${decision.reason ? ` (${decision.reason})` : ''}`);
  return { name, status: 'skipped-policy', reason: decision.reason };
}

decision.reason 是 policy 提供的、面向人的原因(契约里 DatasourceConnectDecision.reason 注释为 "Human-readable reason, surfaced in logs when a connect is denied")。它只进了一条 info 日志,返回值当场被丢弃,引擎从头到尾没听说过这件事。

查询路径。 packages/objectql/src/engine.ts:1756-1765:

private getDriver(objectName: string): IDataDriver {
  const object = this._registry.getObject(objectName);
  if (object?.datasource && object.datasource !== 'default') {
    if (this.drivers.has(object.datasource)) {
      return this.drivers.get(object.datasource)!;
    }
    throw new Error(`[ObjectQL] Datasource '${object.datasource}' configured for object '${objectName}' is not registered.`);
  }
  // ...

这一句错误覆盖了四种完全不同的情况,而调用方无从区分:

实际发生了什么 用户看到
policy 拒绝(套餐 / egress 隔离) Datasource 'x' is not registered.
连不上但被 OS_ALLOW_DRIVER_CONNECT_FAILURE 放行 同上
datasource 根本没声明(拼错了名字) 同上
声明了但 active: false 同上

第三种是作者的 bug,前两种是部署环境的状态 —— 给同一句话是把这两类问题混成一类。

影响

多租户 host 按 plan 挡掉外部 datasource 时,租户的体验是:某些页面报 Datasource 'analytics' is not registered。这句话会把人引向「是不是我 datasource 名字写错了 / 是不是没部署好」,而正确答案是「升级套餐」或「联系管理员开 egress」。support ticket 的成本全在这里。

自建部署里,第 2 种(设了逃生阀)同理:操作者明知数据库连不上才设的旗标,但他的用户看到的仍是 "not registered"。

建议处置

让引擎知道「这个 datasource 被声明过,但当前不可用,以及为什么」。倾向的形状:

  • DatasourceConnectionServiceskipped-policy / failed-* 时,除了返回 ConnectResult,再向引擎登记一条不可用记录(datasource 名 + 类别 + reason)。engine.registerDatasourceDef() 这个 sink 已经存在,可以扩,而不是新开一条通路。
  • getDriver 命中未注册分支时先查这份记录,分情况抛:
    • 有拒绝记录 → Datasource 'analytics' is not available: <policy reason>(policy 自己写的话,host 可控)
    • 有连接失败记录 → Datasource 'analytics' failed to connect at startup: <cause>. The server was started with OS_ALLOW_DRIVER_CONNECT_FAILURE.
    • 无记录 → 保持今天这句(确实是没声明 / 名字写错)
  • 错误里不能泄露连接串、凭据或内部主机名。policy 的 reason 由 host 提供,默认应假定它会被租户看到 —— 这一点要在 DatasourceConnectDecision.reason 的契约注释里写明,现在它只说 "surfaced in logs",与新用法不符。

需要先定的两件事:

  1. 错误码。 目前是裸 Error。这类「配置/环境导致的不可用」应该映射成什么 HTTP 状态?503500 合理,但要确认 REST 层现有的错误分类不会把它归到 4xx(参考 d0fea33 把 ObjectQL ValidationError 映射到 4xx 的做法)。
  2. reason 的可见性边界。 host 侧 policy 的 reason 直接回给终端用户是不是可接受,还是需要 host 显式标记「这条可外露」。倾向后者(默认不外露,opt-in 才进错误消息),但这会让 cloud 侧要改一处 policy 实现。

第 2 点牵涉 cloud 仓库的实际 policy,落地前需要与 cloud 侧对齐。

关联

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