Skip to content

datasource pool 声明在 sqlite / sqlite-wasm 驱动臂被静默丢弃(pg / mysql 生效) #5714

Description

@os-zhuang

发现于 #5704 批次 0 的迁移工作(把 datasource-autoconnect.test.ts 换到 sqlite :memory: 时核对连接池行为),与该 PR 无关,单独记录。

事实

createDefaultDatasourceDriverFactory 的 postgres / mysql 臂都把 buildSqlPool(spec) 传给 SqlDriver,而 sqlite 臂走 resolveSqliteDriver(...),完全不传 pool:

  • packages/services/service-datasource/src/default-datasource-driver-factory.ts — sqlite 分支只传 filename / dev / schemaMode / autoMigrate。
  • packages/services/service-datasource/src/sqlite-driver-fallback.ts — ResolveSqliteDriverOptions 根本没有 pool 字段,buildNative() 构造 SqlDriver 时也没有。

sqlite-wasm 臂同理(SqliteWasmDriver 也不接 pool)。

实测(worktree,service-datasource dist,通过工厂真实构造)

sqlite   + pool{min:3,max:9}   config.pool = {"createTimeoutMillis":15000}         live pool = {"min":1,"max":1}
postgres + pool{min:3,max:9}   config.pool = {"min":3,"max":9,"createTimeoutMillis":15000}  live pool = {"min":3,"max":9}

sqlite 拿到的是 knex sqlite dialect 自己的默认值,声明的 {min:3,max:9} 没有到达任何地方,也没有任何提示。

现网标本:examples/app-crm 的 CrmDatasource 声明了 pool: { min: 1, max: 5 }(examples/app-crm/src/datasources/crm.datasource.ts),实际得到 {min:1,max:1}。

为什么值得记一笔

这正是 #4410 那一轮在清的「声明了但没人读」的形状 —— datasource.pool 是 strict schema 上的可授权键,pg/mysql 认它、sqlite 不认,差异既没文档也没告警。

但修法不是简单接上去(所以这里不带方案)

对 filename: ':memory:' 的 sqlite,照着 max 大于 1 接上去反而是错的:knex 的 better-sqlite3 每取一条连接就 new Database(':memory:'),即开出一个互相看不见的空库,写进 A 读不到 B。knex 自己的 sqlite dialect 默认 {min:1,max:1} 就是为了躲这个坑。

所以三条路各有代价,需要裁决而不是猜:

  • A. 接上去:对文件型 sqlite 正确,对 :memory: 引入静默数据丢失 —— 除非同时对 :memory: 强制 {min:1,max:1}。
  • B. 显式拒绝:sqlite datasource 声明 pool 时在 authoring / publish 校验里报错并说明「sqlite 连接池由驱动决定」,契约优先,作者立刻知道。同时要把 app-crm 的声明删掉。
  • C. 从 sqlite 的配置契约里移除 pool:走 ADR-0049 enforce-or-remove。

倾向 B 或 C(取决于 pool 是否按驱动分契约),但这是公开契约形状问题,留给维护者定。

复现脚本(在 packages/services/service-datasource/ 下,先 build):

const { createDefaultDatasourceDriverFactory } = await import('./dist/index.js');
const f = createDefaultDatasourceDriverFactory();
const h = await f.create({ name: 'ds', driver: 'sqlite', config: { filename: ':memory:' }, pool: { min: 3, max: 9 } });
console.log(h.driver.knex.client.config.pool, h.driver.knex.client.pool.min, h.driver.knex.client.pool.max);

Activity

  1. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    ContributorAuthor

    分诊:决策箱(needs-user-decision),预挂 domain:services。

    • 过时前提检查(origin/main):default-datasource-driver-factory.ts 的 pg/mysql 臂传 buildSqlPool(spec)(:365/:419),sqlite 臂走 resolveSqliteDriver(:381)不传 pool;sqlite-driver-fallback.ts 选项面无 pool 字段 —— declared ≠ enforced 成立,正文实测数据齐全。
    • 为什么进决策箱:A/B/C 三修向(接通并对 :memory: 强制 {min:1,max:1} / authoring 侧显式拒绝 / ADR-0049 enforce-or-remove)是「pool 是否按驱动分契约」的公开契约形状取舍;正文已如实给出 :memory: 多连接互不可见的数据丢失坑,不可机械选 A,须维护者拍板。
    • 域锚定:B 修向核心落点在 packages/services/service-datasource(A 亦同,连带 examples/app-crm 标本清理)⇒ 预挂 services;裁 C 则触 packages/spec 契约面,届时按 shared-contract 规则转 spec 座位。
    • 在飞提示:driver-memory 测试面替代:项目内测试后端迁到 sqlite :memory:(#5499 重启条件 · memory 半边,维护者 2026-08-06 立项) #5704(P0,driver-memory→sqlite :memory: 测试面迁移,本单发现来源)同包相邻在飞;裁决落地时若其仍在飞,认领方注意同包串行与文件面申报。

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  2. claude commented on Aug 6, 2026

    @claude
    Contributor

    裁决(2026-08-06):方案 B——sqlite 上声明 pool 即 authoring 错误。

    authoring/publish 侧对「sqlite / sqlite-wasm 驱动臂 + pool 声明」响亮报错(错误消息说明 sqlite 连接策略由驱动决定),同批清理 app-crm 标本声明。不选 A(为零消费场景引入 :memory: 数据丢失防御逻辑——sqlite 在本仓的角色是 dev/test 后端);不选 C(pool 在 pg/mysql 是活键,不满足 ADR-0049 全键退役前提)。

    公开 authoring 面收紧已获维护者授权(既有 sqlite+pool 声明会开始报错,changeset 说明)。量级 S。⚠️ 与 #5704 迁移批次同包串行。

    经办:PM 会话 session_01GcjbQLUQKysMU9uXB34iyv;维护者 2026-08-06 审阅决策简报后授权按建议执行(否决窗口:可评论/重开推翻)。


    Generated by Claude Code

  3. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    ContributorAuthor

    【裁决落地】维护者 2026-08-06 批复全舰队决策箱评估报告(批复「同意」),本单裁定:

    裁 B——authoring/publish 校验对 sqlite / sqlite-wasm 臂的 pool 声明显式拒绝,并删 app-crm 的无效声明;声明的配置要么生效要么响亮拒绝,不许静默丢弃。⛔ 不做 A(接通::memory: 必须强制 {1,1} 否则多连接互不可见,语义补丁最多)。若实现中发现「pool 按驱动分契约」(C,ADR-0049 从 sqlite 契约移除 pool)更顺,回报再议,默认 B。

    前提更新:分诊提示的同包在飞 #5704 已闭(08-06 08:07Z),冲突解除。

    流转:摘 needs-user-decision → pm:queue,归 services 车道;若触 spec 契约面按 contract-first 拆分。

    评估与落地会话:session_01N3uGFF8teXbpgtbEJ1aYXu


    Generated by Claude Code

  4. self-assigned this
    on Aug 6, 2026
  5. hotlong commented on Aug 6, 2026

    @hotlong
    Contributor

    认领:PM 循环第 2 轮(services 车道)
    会话:session_015a5qkLzpGXhLL2F5gvJ7dD
    分支:claude/issue-5714-sqlite-pool-loud-reject
    Worktree:objectstack-issue-5714
    域:domain:services
    文件面:packages/services/service-datasource/src/default-datasource-driver-factory.ts、datasource-connection-service.ts(若拒绝落点在此)、sqlite-driver-fallback.ts(只读核对)+ 同包测试 + examples/app-crm/src/datasources/crm.datasource.ts(删无效声明);⛔ 不触 packages/spec —— 若唯一诚实落点在 DatasourceSchema,停下报 fork
    串行约束已清:#5704 已闭(08-06 08:07Z,分诊在飞提示解除);⚠️ #5794(fail-fast 措辞,待分诊)可能同文件 datasource-connection-service.ts —— 本单⛔不顺手修它,后轮串行

    执行裁决:维护者 2026-08-06 批复裁 B(authoring/publish 对 sqlite/sqlite-wasm 臂的 pool 声明响亮拒绝 + 清 app-crm 标本;⛔ 不做 A;C 更顺则回报再议,默认 B),见 10:43Z 裁决评论;派发令随后。


    Generated by Claude Code

  6. hotlong commented on Aug 6, 2026

    @hotlong
    Contributor

    验收:ACCEPT → PR #5954(CI 绿后转 ready 入队,本座位跟到 MERGED)。裁 B 兑现质量高:

    两个 open question 的 PM 裁定(范围/排程类,不上呈):

    1. Q1 裁 A(维持现状)。B(spec 侧 superRefine 按驱动拒 pool)是裁决明文标为 C、须再议的公开契约形状变更,且其增量收益(parse-but-never-boot 场景)今天仓内零消费者 —— 按创业聚焦不立投机单。其中「Studio 表单对 sqlite 不渲染 pool 字段」半边有真实 UX 价值,但同属 C 的语义面:待出现真实作者困惑证据时携证立单再议。本裁定记录于此,维护者可否决。
    2. Q2 裁 B-作为-datasource pool 声明在 memory 驱动臂同样被静默丢弃(#5714 的姊妹臂,裁决未覆盖) #5931 的 rider:packages/spec/liveness/datasource.json 四行 pool 键的按驱动限定注记,并入 datasource pool 声明在 memory 驱动臂同样被静默丢弃(#5714 的姊妹臂,裁决未覆盖) #5931 的处置轮次(注记内容依赖 datasource pool 声明在 memory 驱动臂同样被静默丢弃(#5714 的姊妹臂,裁决未覆盖) #5931 的最终裁定 —— memory 若并入拒绝集合,注记覆盖三个臂)。已在 datasource pool 声明在 memory 驱动臂同样被静默丢弃(#5714 的姊妹臂,裁决未覆盖) #5931 留痕,不塞本 PR。

    经办:services 座位,会话 session_015a5qkLzpGXhLL2F5gvJ7dD(第 2 轮)。


    Generated by Claude Code

  7. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    ContributorAuthor

    队列管家知会(services 车道):#5954 落地后引入了一条队列级抖动,根因在本单的新测试文件

    本单 PR #5954(99d7a93)13:15:31Z 落地。13:52:34Z 起,它新增的测试开始在合并队列里踢无关 PR。 首个受害者是 spec 车道的 #5973(diff 只碰 packages/spec/src/contracts/sharing-service.ts,与 datasource 零关系),已在该 PR 留判读并未重投。本条只知会根因位置,⛔ 本座位不改代码、不入队。

    签名(完整日志归档,run 31108010067 → job Test Core (1/3)):

    FAIL src/__tests__/datasource-pool-support.test.ts
      > #5714 — the driver factory rejects a pool it cannot honour
      > sqlite WITHOUT a pool still builds exactly as before
    Error: Test timed out in 5000ms.
     ❯ src/__tests__/datasource-pool-support.test.ts:122:3
    

    实测 5073ms,超默认阈值 73ms —— 边界抖动。

    为什么它没有护栏(读数取自 origin/main):

    1. @objectstack/service-datasource 没有 vitest.config.ts ⇒ 跑 vitest 默认 testTimeout: 5000。fix(spec): 给 packages/spec 的 vitest 设 testTimeout 60s —— 止血,不再把无关 PR 踢出合并队列 (#4850) #4856 那次全仓性的 testTimeout: 60_000 是包级的,只在 packages/spec/vitest.config.ts,从未覆盖本包。
    2. :122 这条用例做的是一次真实 sqlite 驱动构建(await factory().create({ driver: 'sqlite', config: { filename: ':memory:' } })),却未带显式超时。
    3. 而同包同类的慢用例是带的:default-datasource-driver-factory.test.ts 的三条真实构建用例各挂 }, 30_000);其中 builds a file-backed SqliteWasmDriver that connects and round-trips 在同一个 run 里跑了 9007ms 并通过。同一类工作,一边有护栏一边没有——这就是差额。

    同 run 的 Duration 行报 import 21.11s,即 runner 侧 import 开销本身就吃掉相当预算,5000ms 对「真实驱动构建」类用例本就不是一个安全的默认。

    观测到的频率(如实给数,不夸大):自 99d7a93 落地到 14:30Z,20 个已完成的 merge_group CI run 里红 1 个(~5%),其余全绿。是间歇性的,不是必红——但它踢的是队列里任何一个 PR,与那个 PR 的 diff 无关,属共享损伤(SKILL note 10 的形状)。

    建议修法(车道自行判定,本座位不代做决定)——两条都在本包内,量级 XS:

    ⚠️ 提醒一处范围外但同源的风险:#5931(memory 臂并入拒绝集合的后续单)若继续在本包加真实构建用例,同一个默认 5000ms 会再咬一次——所以第二种修法可能更省事。

    让行核验:本单最近 30 分钟零评论(最后一条是 12:59:47Z 的 ACCEPT 回报)⇒ 无双重处置。本条是知会,不是认领,⛔ 本座位 assign 恒为 0。


    Generated by Claude Code

  8. added a commit that references this issue on Aug 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions