Repository navigation
fix: resume a held msnodesqlv8 query that carries on after its last error - #1939
Merged
dhensby merged 1 commit intoOct 18, 2026
Merged
Conversation
…rror A stream paused before its first result set holds msnodesqlv8's query back. The request takes an error with more=false to end the batch, and takes the query up again if it goes on to a result set after all. A stream resumed in between left the query held at that result set, so the request never completed and its connection was never released. The query is now resumed once it reaches the result set. That happens a tick after 'meta', because msnodesqlv8 fetches as soon as it is resumed, and again once 'meta' is handled. No msnodesqlv8 version tested reaches this: 2.7, 3.1, 4.5, 5.2.1 and 5.6 against SQL Server 2022. Before 5.6 an error ahead of the first result set ends the query, and 5.6 gives one that does not with more=true. It is the case the request already provides for at 'meta', since the reader of every version before 5.6 can give a statement's last error more=false and carry on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟢 Approval recommended
The focused change preserves existing lifecycle guards and has regression coverage for the intended recovery sequence.
No open issues 🎉
What changed in this PR
Completes msnodesqlv8’s recovery handling for a held query that reaches a result set after an error marked as final.
Changes:
- Defers the driver resume until after
metahandling to avoid duplicate fetches. - Adds regression coverage for resume timing, streamed rows, and connection release.
| File | Description |
|---|---|
| test/common/unit.js | Tests the error-to-result-set recovery sequence. |
| lib/msnodesqlv8/request.js | Schedules a guarded resume when a detached query continues. |
Review effort: Balanced
|
🎉 This PR is included in version 12.7.10 🎉 The release is available on: Your semantic-release bot 📦🚀 |
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.
What
A stream paused before its first result set holds msnodesqlv8's query back (
_pauseStream). When msnodesqlv8 gives an error with more=false, the request marks the querydetached, and while it is detached the request makes no pause, resume or cancel. The'meta'handler takes the query up again if it goes on to a result set after all: it clearsdetachedand replays a held cancel. It never resumes the driver, though. So the query stayed parked at'meta'if the stream was resumed while it was detached. The request then never completed, and its connection was never released.The fix: when
'meta'clearsdetached, a stream that is no longer paused, with a driver still paused and nothing being cancelled, resumes the query. The resume runs a tick later, throughsetImmediate, and_resumeStreamchecks every condition again when it runs. It cannot be synchronous: msnodesqlv8'sresume()fetches at once, and the reader callsdispatch()again as soon as'meta'is handled. A resume made inside'meta'would therefore fetch twice, which is the failure in #1924.Reachability
This path is not reachable on any msnodesqlv8 version tested. It comes from code review and is not a reported hang.
I probed it on Windows CI against SQL Server 2022 with msnodesqlv8 2.7.0, 3.1.0, 4.5.0, 5.2.1 and 5.6.0. The probe used a throwaway fork branch, which has since been deleted.
waitforbetween the failing statement and the select. The batches usedraiserror,print, a PK violation, divide by zero, row counts before and after the error,exec(),sp_executesql, procedures with NOCOUNT on and off, and a select ahead of the error.error(false)and then'free'.'meta'.npm run test-msnodesqlv8passed on all five versions with this change.The request already provides for this case at
'meta'. The reader of every msnodesqlv8 version before 5.6 can give a statement's last error more=false and carry on:begin()routes a warning from execute withmore = i < errorCount - 1. This change finishes the job that handling started.Related
'meta'. That covers this case too, and the new test passes on its branch with no lib change. Whichever lands second should keep a single deferred resume. Once fix: stop msnodesqlv8 streams failing when resumed while their query executes #1928 is rebased, thesetImmediateadded here becomes redundant there, and the conflict is easy to resolve.cancel()andpauseAndCancelDriver()near this code, without overlapping it.Tests
A new case in
msnodesqlv8 query eventsplays this sequence: fail with more=false, the stream resumes,'meta', then a row,'done'and'free'. It asserts:'meta'is handled, which catches a synchronous resume;['pause', 'resume'];The test fails on master.
🤖 Generated with Claude Code