Skip to content

Comments

Cancel queries on client disconnect#232

Merged
fuziontech merged 6 commits intomainfrom
feat/cancel-query-on-disconnect
Feb 18, 2026
Merged

Cancel queries on client disconnect#232
fuziontech merged 6 commits intomainfrom
feat/cancel-query-on-disconnect

Conversation

@fuziontech
Copy link
Member

Previously, if a client disconnected while a query was running, the worker would continue executing the query until completion. This was especially problematic for long-running analytical queries.

This PR implements end-to-end query cancellation:

  1. 'clientConn' now maintains a base context that is cancelled as soon as 'serve()' or the control plane's connection handler exits.
  2. All query execution contexts are derived from this base context.
  3. 'FlightExecutor' monitors its own lifecycle and cancels all in-flight gRPC requests to workers when closed.
  4. Workers correctly respect gRPC context cancellation and stop DuckDB execution.

fuziontech and others added 6 commits February 17, 2026 18:10
This PR ensures that active database queries are stopped immediately when a client connection is lost.

Key changes:
- Added a connection-level context to 'clientConn' that is cancelled when the session ends.
- Updated 'queryContext()' to derive from the connection context, ensuring all queries (Query, Exec, Cursor operations) stop when the client disconnects.
- Updated 'FlightExecutor' to correctly propagate cancellation to worker processes via gRPC context cancellation.
- Hardened constructors and destructors to prevent nil-pointer panics in unit tests.
The previous commits set up a context chain (clientConn.ctx → queryContext
→ FlightExecutor.mergedContext) but nothing actually detected TCP disconnects
while a query was in-flight. The message loop is single-threaded, so it only
noticed disconnects after the current query completed.

This commit adds a Peek-based disconnect monitor goroutine that runs during
query execution. It uses bufio.Reader.Peek(1) with short read deadlines to
detect TCP FIN/RST without consuming bytes from the stream. When a disconnect
is detected, the connection context is cancelled, propagating through the
context chain to cancel gRPC calls on Flight SQL workers.

Key changes:
- startDisconnectMonitor() in conn.go polls the connection during queries
- RunMessageLoop() now defers cc.cancel() for proper cleanup in control plane
- FlightExecutor context merging extracted into mergedContext() helper
- Tests for disconnect detection, stop-before-disconnect, and data preservation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cursors span multiple message loop iterations (FETCH, FETCH, CLOSE).
The disconnect monitor uses bufio.Reader.Peek which would race with
readMessage between FETCH calls. Use queryContextForCursor() which
skips the monitor — disconnect detection still works via c.ctx
cancellation when the connection handler exits.

Also add a comment at the COPY dispatch site noting it must stay
above queryContext() since handleCopyIn reads from c.reader directly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When pipelined data is in the bufio.Reader buffer, Peek(1) returns
instantly without hitting the network, causing a tight spin loop.
Add a 50ms throttle on the success path to prevent CPU waste.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@fuziontech fuziontech merged commit cf3d7db into main Feb 18, 2026
11 checks passed
@fuziontech fuziontech deleted the feat/cancel-query-on-disconnect branch February 18, 2026 02:59
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.

1 participant