Skip to content

fix(driver-sql): give the bounded connection attempt an accurate error message (#3769) - #3791

Merged
os-zhuang merged 2 commits into
mainfrom
claude/objectql-driver-connection-error-jwicgq
Jul 28, 2026
Merged

fix(driver-sql): give the bounded connection attempt an accurate error message (#3769)#3791
os-zhuang merged 2 commits into
mainfrom
claude/objectql-driver-connection-error-jwicgq

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes #3769#3781 做完了"限时"那一半,这个 PR 做"说实话"那一半。

问题

#3781pool.createTimeoutMillis 把连接尝试限在 10s,消掉了 30 秒卡死,但保留了 knex 自己的措辞:

Timeout acquiring a connection. The pool is probably full

池并没有满 —— 是服务端从未完成握手。这句话会把运维引去调 pool.max,而坏掉的是网络。这跟 #3741 那个启动守卫是同一类缺陷:报出的错误跟真实原因相距很远

改动

SqlDriver 现在同时设置该 dialect 自己的连接超时,它失败时说的是实情:

client key 消息
pg / postgres / postgresql / cockroachdb connectionTimeoutMillis timeout expired
mysql / mysql2 connectTimeout connect ETIMEDOUT

携带超时要求 connection 是对象,所以 URL 字符串被搬进该 dialect 的 URL 槽(pg 用 connectionString,mysql2 用 uri)。两种形式都实测过:连接尝试仍然打到 URL 自己的 host/port(用黑洞监听器的 accept 计数验证),?sslmode=require 仍被正确解析。SQLite 完全不碰 —— 打开一个文件没有握手可超时。函数式 connection(knex 的 per-acquire provider)也不碰:宿主自己在造连接,超时归它管。

两个上限故意不相等

这是写严格断言时才发现的:它们会竞争,而 knex 赢平局。两个都设 10s 时,池超时先触发,准确消息永远说不上话 —— 测试如实变红,我才发现。

所以:dialect 超时是有效上限(10s),池超时是严格更宽的兜底(10s → 15s),只有在某 dialect 没有连接超时旋钮(SQLite)或忽略我们设的值时才会走到。

driver.config 保持作者传入的形状

重写只作用于交给 knex 的那份。两个既有读取方依赖原形状:serve.ts 的启动横幅,以及 createDatabase()(解析 URL 换成维护库)。加了测试钉住这个不变量。

createDatabase() 自己的 admin 连接也补上了同样的上限 —— 它在启动期连的正是那台我们已经怀疑连不上的服务器,不该成为唯一还等 30 秒的地方。

验证

  • sql-driver-connect-bound.test.ts 扩到 12 例:pg/mysql2 的 URL 搬迁形状、对象连接补字段不扰动其余、宿主显式值被尊重、sqlite 不注入、函数式 connection 不动、driver.config 形状不变,以及黑洞端点用例现在断言措辞(toContain('timeout expired') + not.toContain('pool is probably full'))而不只是"抛了错"。
  • driver-sql 386 / cli 673 / objectql 1128 / runtime 661,pnpm build 71/71 全绿。

顺带发现的既存 bug(未在本 PR 修)

启动横幅的 driver 行显示 Driver: SqlDriver(pg) → (unknown),URL 丢了。我一开始以为是自己引入的,在干净 main 上复现后确认是既存问题:

189:  Driver:  SqlDriver(pg)  → (unknown)      ← 干净 main,同样如此

describeRegisteredDriver 读到了 cfg.client(所以标签对)却没读到 URL。与本次改动无关,按 PD #10 记在这里而不是夹带修复。


Generated by Claude Code

…r message (#3769)

#3781 bounded a connection attempt at 10s via pool.createTimeoutMillis, which
stopped the 30s hang but kept knex's own wording: "Timeout acquiring a
connection. The pool is probably full". The pool is not full — the server never
completed the handshake — so that message sends an operator to tune pool.max
while the network is what is broken. Same defect class as #3741's boot guard: an
error that reads nothing like its cause.

SqlDriver now also sets the DIALECT'S OWN connect timeout, which fails with a
message that names what happened:

  pg / postgres / postgresql / cockroachdb → connectionTimeoutMillis → "timeout expired"
  mysql / mysql2                           → connectTimeout          → "connect ETIMEDOUT"

Carrying the timeout requires `connection` to be an object, so a URL string moves
into the dialect's URL slot (connectionString for pg, uri for mysql2). Measured
against a black-holing listener: both forms still reach the URL's own host/port
and still honour ?sslmode=require. SQLite is untouched — a file open has no
handshake to time out. A function-valued connection is left alone: the host is
building each connection itself.

The two bounds are deliberately UNEQUAL. They race and knex wins a tie, so equal
values let the pool timeout fire first and the accurate message is never seen —
caught by the black-hole test, which now asserts the wording rather than merely
that something threw. The dialect timeout is the effective bound at 10s; the pool
timeout is a strictly looser backstop, 10s → 15s, reached only by a dialect with
no connect-timeout knob or one that ignores ours.

driver.config keeps the shape the author passed — the rewrite applies only to
what knex receives. Two existing readers depend on that: serve.ts's startup
banner and createDatabase(), which parses the URL to swap in the maintenance
database. A test pins it. createDatabase()'s own admin connection now gets the
same bound; it is opened during boot against the very server we already suspect
is unreachable.

Verified: driver-sql 386, cli 673, objectql 1128, runtime 661.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T9VBCNvg9Zixrf1A9Rnwdd
@vercel

vercel Bot commented Jul 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Jul 28, 2026 5:42am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/m labels Jul 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/driver-sql.

8 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/data-modeling/drivers.mdx (via @objectstack/driver-sql)
  • content/docs/getting-started/glossary.mdx (via @objectstack/driver-sql)
  • content/docs/plugins/anatomy.mdx (via @objectstack/driver-sql)
  • content/docs/plugins/packages.mdx (via @objectstack/driver-sql)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/driver-sql)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/driver-sql)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/driver-sql)
  • content/docs/releases/implementation-status.mdx (via @objectstack/driver-sql)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

…— document the two connect-timeout defaults

The drift check flagged drivers.mdx: it said SqlDriver 'accepts a Knex.Config
object verbatim', which #3791 makes false — two connect-timeout defaults are now
injected. Records both, why they are deliberately unequal, and how to override.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T9VBCNvg9Zixrf1A9Rnwdd
@os-zhuang
os-zhuang marked this pull request as ready for review July 28, 2026 05:49
@os-zhuang
os-zhuang merged commit 7457a09 into main Jul 28, 2026
16 checks passed
@os-zhuang
os-zhuang deleted the claude/objectql-driver-connection-error-jwicgq branch July 28, 2026 05:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

黑洞型数据库端点:每个查询挂 30 秒,然后报一句指向错误方向的 "the pool is probably full"

2 participants