Skip to content

fix(ws): fail in-flight queries when the connection drops or times out - #284

Open
Gioee wants to merge 1 commit into
mainfrom
fix/ws-inflight-query-on-disconnect
Open

Gioee wants to merge 1 commit into
mainfrom
fix/ws-inflight-query-on-disconnect

Conversation

@Gioee

@Gioee Gioee commented Oct 19, 2026

Copy link
Copy Markdown
Member

Fixes #283

Problem

On the WebSocket transport, transportCommands waits for the socket.io ack of GET /v2/weblite/sql. If the socket disconnects while a query is in flight (for example when the gateway closes the connection because the request exceeds its maxHttpBufferSize, or on a ping timeout):

  • the ack never fires;
  • the disconnect handler only calls the connect callback, which already ran on connect, so the error is logged (SQLiteCloudConnection.connect - error connecting … Disconnected) but never reaches the command;
  • close() clears the operations queue, so nothing else settles it;
  • unlike the TLS transport there is no timeout to fall back on.

The result is a db.sql(...) promise that never resolves nor rejects.

A side effect of calling the connect callback a second time is that the base connect() wrapper calls the queue's done twice, and Database receives a second callback for the same connection.

Fix (src/drivers/connection-ws.ts)

  • Each command registers itself in pendingCommands until its ack arrives. Its callback is guarded so it runs exactly once (response, timeout or connection loss).
  • disconnect, connect_error and error close the socket and fail every pending command with the same error the connect callback used to receive (ERR_CONNECTION_ENDED with the socket.io reason as cause, the attachment limit error, etc.).
  • close() fails pending commands with ERR_CONNECTION_CLOSED.
  • The connect callback is called only once, for the outcome of the connection attempt (same as the TLS transport, where later errors go to the command in progress).
  • config.timeout (default 300 s) now applies to WebSocket requests too: when it elapses the command fails with ERR_CONNECTION_TIMEOUT and a late ack is ignored. The connection stays open, since the gateway handles every request independently.

The request size limit itself is a gateway configuration and is out of scope here: with this change, whatever the limit is, the driver reports an error instead of hanging.

Testing

New unit tests in test/connection-ws-unit.test.ts (mocked socket.io):

  • the command in flight fails with ERR_CONNECTION_ENDED on disconnect, and the connect callback is not called again;
  • a disconnect before connect is still reported to the connect callback;
  • close() fails the command in flight;
  • the command fails after timeout and a late response is ignored;
  • a completed command is not affected by a later disconnect or timer.

Three of these fail on main and pass with the fix.

Manual check against the staging gateway, sending SELECT length(X'…') payloads above its message size limit:

Payload main this branch
5 MB pending after 30 s rejected ERR_CONNECTION_ENDED (transport error) in ~1.8 s
20 MB pending after 30 s rejected ERR_CONNECTION_ENDED (transport error) in ~2.8 s
80 MB pending after 30 s rejected ERR_CONNECTION_TIMEOUT (test config timeout 10 s)

connection-ws, blob-transport, database, statement and utilities test suites pass.

On the WebSocket transport a query waits for the socket.io ack of
'GET /v2/weblite/sql'. When the socket disconnected mid-query the ack
never fired and the error was only sent to the connect callback, which
had already run, so the query promise stayed pending forever.

- track the commands waiting for an ack and fail them with the
  disconnect/connection error when the socket goes away or close() is
  called
- report the connect callback only once, for the outcome of the
  connection attempt
- honour config.timeout on the WebSocket transport too

Fixes #283
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.28571% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.71%. Comparing base (bb3026e) to head (1bd46d0).

Files with missing lines Patch % Lines
src/drivers/connection-ws.ts 94.28% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #284      +/-   ##
==========================================
+ Coverage   85.23%   85.71%   +0.47%     
==========================================
  Files          11       11              
  Lines        1131     1155      +24     
  Branches      374      375       +1     
==========================================
+ Hits          964      990      +26     
+ Misses        167      153      -14     
- Partials        0       12      +12     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Gioee
Gioee requested a review from danielebriggi October 22, 2026 13:58

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WebSocket: a dropped connection leaves the in-flight query pending forever

2 participants