Repository navigation
fix(ci): stop close()-mid-flight engine abort (#53) and absorb ResultSet teardown (#54) - #56
Merged
Conversation
… ResultSet teardown (chdb-io#54) Two failures were red on main; both stem from a lifecycle teardown racing an op that the layer above assumes has already settled. chdb-io#53 — test/async-stress 'close session during in-flight query' hung 120s on macos-14/Node-20, then poisoned 48 later v3 tests with code 236 (engine ABORTED). A plain queryAsync (no signal/timeout) took withAbortTimeout's fast path, which skipped trackNative, so pendingNativeOps was empty and Session.close() ran CloseConnection immediately — mid-flight. Releasing a connection while its query is still executing on the libuv worker aborts the in-process engine and, on some platforms, leaves the worker blocked inside chdb_query so its promise never settles and the caller's await hangs to the test timeout. Fix: track EVERY native async op (query and insert), not just the abort/timeout ones, so close() defers teardown until in-flight ops drain. chdb-io#54 — the connection/parity job exited non-zero on an unhandled 'Error: ResultSet has been closed' even though all 27 files passed. clickhouse-js's ResultSet.close() tears down via this._stream.destroy(new Error('ResultSet has been closed')); for a never-consumed result (a non-streamable-format query whose stream() threw, closed in a finally) nothing has attached an 'error' listener to the Readable we return, so Node escalates that synthetic teardown error to an uncaughtException, surfacing async and blamed on the next test. Fix: attach a benign 'error' listener to the materialized buffer Readable — it has no genuine async error source, so the only 'error' it can receive is exactly that external teardown signal; real consumers (Stream.pipeline in ResultSet.stream) attach their own handler, so genuine errors still propagate. Also gitignore the parity runner's .chdb-runner checkout. Verification: full v3 suite 174 passed / 4 skipped (lifecycle race ~2s); clickhouse-js parity runner exits 0 with 0 unhandled errors (180 passed | 41 skipped). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ShawnChen-Sirius
added a commit
that referenced
this pull request
Jul 7, 2026
…w integrations Additive minor release on top of 3.1.0 (2026-06-24). No breaking changes; existing 3.1.0 APIs continue to work unchanged. libchdb bits are unchanged (chdb-core v26.5.1-rc.1 packaged as @chdb/lib-* 26.5.2 — no republish needed, the subpackage-matrix job will idempotent-skip). ## What's new ### Layer 3 fluent query builder SDK (#55, #59) First-class typed fluent query API — Kysely-/Drizzle-style: const db = connect({ session }) const rows = await db .from('users') .where(r => r.age.gt(18)) .select('id', 'name') .execute() - `src/layer3/{builder,execute,compile,dialect,introspection,connect,types,codegen}/` - `chdb-gen-types` CLI — generates row types from a schema (Drizzle / Prisma / raw SQL sources) - `.stream()` terminal with server-side parameter binding - Arrow C Data Interface support: register JS columnar data as `arrowstream()` tables - 12 new test files under `test/v3/layer3/` ### Cross-language agent-tool contract + 2 framework adapters (#61, #66) - `chdb/ai-sdk` — Vercel AI SDK tools (`generateText`/`streamText`) - `chdb/mastra` — Mastra tools + `ChDBVector` (HNSW RAG store) + `ChDBStore` - Both are thin wrappers over `ChDBTool.call()` from `integrations/agents/`, which implements the cross-language contract vendored from `chdb-io/chdb` (`chdb/agents/CONTRACT.md`, chdb-io/chdb#597). - 7 canonical tools: `run_select_query` / `list_databases` / `list_tables` / `describe_table` / `get_sample_data` / `list_functions` / `attach_file` - 5 pillars enforced: engine-level readonly=2 (P1), value-vs-identifier separation (P2), truncation flag (P3), error envelope for model self-correction (P4), resource/source controls (P5) - Behavior verified against the shared `conformance/cases.jsonl` fixture ### `chdb/hypequery` DatabaseAdapter (#63, #65) Lets `@hypequery/clickhouse` query builder run on embedded chDB instead of a remote ClickHouse server. Uses hypequery's own `substituteParameters` (imported, not copied) so rendered SQL stays byte-identical to hypequery's built-in HTTP adapter. ### `chdb/connection` stabilization for @clickhouse/client 1.23.0 - `peerDependency: "@clickhouse/client: >=1.23.0"` (optional) — old client triggers a clear npm warning instead of a runtime TS error - Parity runner switched to track `ClickHouse/clickhouse-js` release tag `client-1.23.0` (per the sync-policy table; #52 shipped the initial hook) - Runner auto-detects upstream Vitest layout (pre-/post-clickhouse-js#931) - Runner re-synced when clickhouse-js reorganized into a monorepo (#62) ### AI-discovery docs (#60) - Top-level `AGENTS.md` + `llms-full.txt` - Per-subpath `AGENTS.md` (`src/layer3/`, `src/connection/`, etc.) - All packaged in `files` so agents crawling the installed tarball find them ## Fixes / infrastructure - Bind `null` / `undefined` query params as SQL `\N` (contributor @dfrankland, #68) — matches `@clickhouse/client` behavior - `close()`-mid-flight engine abort + ResultSet teardown race (#56) - `ChDBVector` HNSW granularity / filter binding / dimension guard (#61) - Release workflow: publish → `unverified` dist-tag → clean-install verify matrix → promote to `latest` (#64). A broken release now never reaches `latest` users. - CI: cache deps + harden npm registry fetch against transient resets ## User-facing installation matrix ``` npm install chdb # base + Layer 3 SDK npm install chdb @clickhouse/client # + chdb/connection npm install chdb @hypequery/clickhouse # + chdb/hypequery npm install chdb ai zod # + chdb/ai-sdk npm install chdb @mastra/core zod # + chdb/mastra ``` Native libchdb: chdb-core v26.5.1-rc.1 (packaged as @chdb/lib-* 26.5.2), unchanged from 3.1.0. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two workflows were red on
main(commit08ffdb2, the #52 merge). Both failures come from a lifecycle teardown racing an operation that the layer above assumes has already settled — fixed here in one change each.Closes #53, closes #54.
#53 —
testred: async-stress 120s hang on macos-14/Node-20The
closing a session while its async query is in flight (100x)case hung the full 120 s onmacos-14/20.x, then the abandoned in-flight op poisoned 48 later v3 tests withCode: 236 … server is shutting down (ABORTED).Root cause: a plain
queryAsync(nosignal/timeout) takeswithAbortTimeout's fast path, which skippedtrackNative. SopendingNativeOpswas empty andSession.close()ranCloseConnectionimmediately, mid-flight. Releasing a connection while its query is still executing on the libuv worker aborts the in-process engine and, on some platforms, leaves the worker blocked insidechdb_queryso its promise never settles → the caller'sawaithangs to the test timeout. (The test's own header comment predicted exactly this: "plain queryAsync is not tracked for drain".)Fix: track every native async op (query and insert), not just the abort/timeout ones, so
close()defers teardown until in-flight ops drain. This structurally removes the mid-flight-release race rather than papering over the timing.#54 —
connectionred: unhandledResultSet has been closedinparityThe
parityjob exited non-zero on 1 unhandled error even though all 27 test files passed:Root cause: clickhouse-js's
ResultSet.close()tears down viathis._stream.destroy(new Error("ResultSet has been closed")). For a never-consumed result (the "format is not stream-able" test —stream()throws, thenresult.close()in itsfinally), nothing has attached an'error'listener to theReadablewe hand back, so Node escalates that synthetic teardown error to anuncaughtException. It surfaces asynchronously and vitest blames the next test (can pause response stream).Fix: attach a benign
'error'listener to the materialized bufferReadable. That buffer has no genuine async error source (read()only ever pushes), so the only'error'it can ever receive is exactly that external teardown signal — absorbing it makesclose()idempotent as the upstream contract expects. Real consumers (Stream.pipelineinResultSet.stream) attach their own error handler, so genuine errors still propagate. Also gitignores the parity runner's.chdb-runnercheckout.Verification
node tests/clickhouse-js/runner.mjs, clickhouse-js@main injected withChdbConnection): exits 0 with 0 unhandled errors —Test Files 27 passed | 2 skipped,Tests 180 passed | 41 skipped.🤖 Generated with Claude Code