Skip to content

fix: a repeated cancel's refusal also comes before the query's HY008 - #496

Merged
TimelordUK merged 1 commit into
masterfrom
fix/cancel-refusal-before-hy008
Oct 22, 2026
Merged

TimelordUK merged 1 commit into
masterfrom
fix/cancel-refusal-before-hy008

Conversation

@TimelordUK

Copy link
Copy Markdown
Owner

Follow-up to #495. After that merge, cancel/again-after-confirm-at-100 failed once more on Linux CI (run):

  action cancel
  cancel-cb ok
  action cancel again
+ error HY008 0 Operation canceled more=false
  cancel-cb error cannot cancel query (cancel already requested) id 0
- error HY008 0 Operation canceled more=false

Cause

#495 held native results until the accepted cancel's callback had run, but not for the refusal of a repeated cancel. The sequence that slipped through:

  1. The first cancel's callback runs.
  2. Inside it, the scenario cancels again. The refusal is scheduled with setImmediate, so it runs in the next loop iteration's check phase.
  3. The first cancel settles. Nothing is held yet.
  4. The fetch's HY008 completion lands in the poll phase, before that check phase. Nothing is pending any more, so it runs straight away, ahead of the refusal.

Fix

The notifier now counts every cancel callback still to run, accepted or refused (holdForCancel). It releases what is waiting one tick after the count gets back to 0.

Test

test/cancel-order.test.js uses a stub connection and needs no server. Its first case reproduces the CI interleaving deterministically: a process.nextTick registered from the cancel callback runs after that callback but before the refusal's setImmediate.

  • On master's notifier, that case fails.
  • With this change, all 5 cases pass.

Local runs (WSL, SQL Server 2022): contract, replay, cancel-order, cancel-regression, querycancel and cancel-stress, 386 passing.

🤖 Generated with Claude Code

#495 held a native result until the accepted cancel's callback had run,
but not the refusal of a repeated cancel. On CI after the merge,
cancel/again-after-confirm-at-100 still failed once: the second cancel,
sent from the first cancel's callback, is refused a tick on (setImmediate),
and the fetch's HY008 landed in the poll phase in between. With nothing
pending any more, it ran first.

The notifier now counts every cancel callback still to run, accepted or
refused, and releases what waits once the count is back to 0.

test/cancel-order.test.js pins the order with a stub connection and no
server. Its first case uses process.nextTick from the cancel callback to
put the result exactly where CI saw it: it fails on #495's notifier and
passes with this one.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@TimelordUK
TimelordUK merged commit 7b8f500 into master Oct 22, 2026
11 checks passed
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