一句话说明
ADR-0062 D1 要求「exactly one definition → live driver code path」。构造那一半已经收敛(standalone-stack 走共享的 createDefaultDatasourceDriverFactory),但 connect 与连接失败判决那一半没有:default driver 被包成 DriverPlugin 在引擎前注册,它的 connect() 发生在 ObjectQLEngine.init();声明式 datasource 的 connect() 发生在 DatasourceConnectionService。于是「连不上该怎么办」这个决定有两份独立实现。
事实核实
packages/runtime/src/standalone-stack.ts:168-245 的注释自己划了这条线:
用户可见的四种 kind 走 SHARED datasource driver factory(ADR-0062)…… This stack still owns what's standalone-specific: URL→config translation, filesystem prep (mkdir), and DriverPlugin registration (pre-engine — unchanged).
两条路径:
|
default |
声明式 datasource |
| 构造 |
createDefaultDatasourceDriverFactory().create() ✅ 共享 |
同上 ✅ |
| 注册 |
new DriverPlugin(driver),引擎 init 前 |
engine.registerDriver(),在 connect() 内 |
| connect |
ObjectQLEngine.init()(engine.ts:1924) |
DatasourceConnectionService.connect() |
| 失败判决 |
DriverConnectError + 聚合(#3741) |
handleFailure()(datasource-connection-service.ts:385) |
| 逃生阀 |
OS_ALLOW_DRIVER_CONNECT_FAILURE |
同一个(#3816 起) |
#3816 之后两边的行为已经对齐了(同一判据方向、同一旗标、同一 emitDegradedBootBanner),但那是靠两处代码各自写对来维持的一致,不是结构上的一致。
影响
不是当下的 bug,是结构性漂移风险:任何一次对连接失败策略的改动都要记得改两个地方。这次 #3758 就是活例子 —— #3741 在 init() 那层修了,DatasourceConnectionService 那层的边界原封不动地留了三个月,直到有人再报一次。第三次改动没有理由更幸运。
同样分叉的还有:pool 生命周期(D5 说 DatasourceConnectionService owns connect/disconnect,但 default 的 disconnect 走 DriverPlugin/kernel shutdown)、连接策略(DatasourceConnectPolicy 完全看不到 default)。
建议处置
按 ADR-0062 D1 的原计划把 default 收敛到 DatasourceConnectionService。ADR 自己标注了这是风险最高的一步(§Risk:"Phase 1's default-driver refactor is the riskiest single step (the hot boot path for all apps) — mitigated by landing auto-connect for declared datasources first, refactoring default onto the shared service last, behind the dogfood gate")。前置条件现在都满足了:声明式 auto-connect 已上线并有 dogfood gate 覆盖,失败策略也已在两边对齐 —— 意味着这次重构是纯搬迁,不带行为变更,是它风险最低的时刻。
需要一并想清楚的:
- 注册时机。
default 目前是引擎 init 之前注册(DriverPlugin),声明式 datasource 是 AppPlugin.start() 里。default 必须在 schema sync 之前就位,不能简单挪到 start()。
DriverPlugin 是否保留。它还承担 --fresh / 测试注入的口子,不宜直接删。
DatasourceConnectPolicy 要不要看到 default。倾向不要 —— policy 是给多租户 host 做 egress 隔离的,主库不在它管辖范围内;但这需要写进 ADR 而不是靠默认行为。
关联
一句话说明
ADR-0062 D1 要求「exactly one definition → live driver code path」。构造那一半已经收敛(
standalone-stack走共享的createDefaultDatasourceDriverFactory),但 connect 与连接失败判决那一半没有:defaultdriver 被包成DriverPlugin在引擎前注册,它的connect()发生在ObjectQLEngine.init();声明式 datasource 的connect()发生在DatasourceConnectionService。于是「连不上该怎么办」这个决定有两份独立实现。事实核实
packages/runtime/src/standalone-stack.ts:168-245的注释自己划了这条线:两条路径:
defaultcreateDefaultDatasourceDriverFactory().create()✅ 共享new DriverPlugin(driver),引擎 init 前engine.registerDriver(),在connect()内ObjectQLEngine.init()(engine.ts:1924)DatasourceConnectionService.connect()DriverConnectError+ 聚合(#3741)handleFailure()(datasource-connection-service.ts:385)OS_ALLOW_DRIVER_CONNECT_FAILURE#3816 之后两边的行为已经对齐了(同一判据方向、同一旗标、同一
emitDegradedBootBanner),但那是靠两处代码各自写对来维持的一致,不是结构上的一致。影响
不是当下的 bug,是结构性漂移风险:任何一次对连接失败策略的改动都要记得改两个地方。这次 #3758 就是活例子 —— #3741 在
init()那层修了,DatasourceConnectionService那层的边界原封不动地留了三个月,直到有人再报一次。第三次改动没有理由更幸运。同样分叉的还有:pool 生命周期(D5 说
DatasourceConnectionServiceowns connect/disconnect,但default的 disconnect 走DriverPlugin/kernel shutdown)、连接策略(DatasourceConnectPolicy完全看不到default)。建议处置
按 ADR-0062 D1 的原计划把
default收敛到DatasourceConnectionService。ADR 自己标注了这是风险最高的一步(§Risk:"Phase 1'sdefault-driver refactor is the riskiest single step (the hot boot path for all apps) — mitigated by landing auto-connect for declared datasources first, refactoringdefaultonto the shared service last, behind the dogfood gate")。前置条件现在都满足了:声明式 auto-connect 已上线并有 dogfood gate 覆盖,失败策略也已在两边对齐 —— 意味着这次重构是纯搬迁,不带行为变更,是它风险最低的时刻。需要一并想清楚的:
default目前是引擎 init 之前注册(DriverPlugin),声明式 datasource 是AppPlugin.start()里。default必须在 schema sync 之前就位,不能简单挪到 start()。DriverPlugin是否保留。它还承担--fresh/ 测试注入的口子,不宜直接删。DatasourceConnectPolicy要不要看到default。倾向不要 —— policy 是给多租户 host 做 egress 隔离的,主库不在它管辖范围内;但这需要写进 ADR 而不是靠默认行为。关联