Skip to content

fix: make cancel() actually cancel the in-flight query#733

Open
joshmarkovic wants to merge 1 commit into
dbt-msft:masterfrom
joshmarkovic:fix/cancel-noop
Open

fix: make cancel() actually cancel the in-flight query#733
joshmarkovic wants to merge 1 commit into
dbt-msft:masterfrom
joshmarkovic:fix/cancel-noop

Conversation

@joshmarkovic

Copy link
Copy Markdown
Contributor

Problem

cancel() only wrote a debug log line. It never cancelled anything.

When a run is interrupted (Ctrl-C) or one thread hits an error, dbt-core calls cancel() on the other open connections. Because this adapter's cancel() was a no-op, those queries kept running on the server until they finished on their own.

Fix

  • add_query() now stores the active cursor on the connection while a statement is executing, and always clears it afterwards, including on failure.
  • cancel() looks up that cursor and calls Cursor.cancel() on it. pyodbc documents this as safe to call from another thread (it issues SQLCancel). mssql-python cursors are used the same way when they support it.
  • Cancellation stays best-effort: if no query is in flight, the cursor has no cancel(), or the call fails because the statement just finished, it logs at debug level and moves on.

Testing

  • 7 new unit tests: cancelling a live cursor, the no-op paths, error swallowing, and that add_query() registers the cursor during execution and clears it on both success and failure.
  • Full unit suite passes (275 tests) on this branch rebased onto current master. ruff, black, and mypy are clean.

SQLServerConnectionManager.cancel() was a no-op (it only logged), so
dbt-core's cancel_open() could not stop sibling queries on Ctrl-C or
when another thread failed; in-flight statements kept running
server-side, holding locks.

Track the in-flight cursor on the Connection during add_query and call
Cursor.cancel() on it from cancel(). pyodbc exposes Cursor.cancel()
(issuing SQLCancel, designed for cross-thread use) and mssql-python's
cursor is used the same way when supported. Cancellation is best-effort:
it no-ops when no statement is in flight, the cursor is gone, or the
backend cursor lacks cancel(), and it swallows errors from a statement
that completed between lookup and cancel.

Add unit tests for cancel() (in-flight, absent, unsupported, error
paths) and for add_query registering then clearing the cursor.
@joshmarkovic joshmarkovic marked this pull request as ready for review July 3, 2026 00:25
@axellpadilla

Copy link
Copy Markdown
Collaborator

Please check the conflict @joshmarkovic

@axellpadilla axellpadilla added this to the v1.10.1 milestone Jul 3, 2026
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.

2 participants