Skip to content

Explore timing over the fake driver; a paused query holds back what a call in flight brings back - #485

Merged
TimelordUK merged 2 commits into
masterfrom
test/timing-explorer
Oct 17, 2026
Merged

TimelordUK merged 2 commits into
masterfrom
test/timing-explorer

Conversation

@TimelordUK

Copy link
Copy Markdown
Owner

The fix (v6 behaviour)

A pause that landed while a native call was in flight still let that call's events out when it returned. That meant the meta after execute or after the next result set, and an error from a fetch or nextResultSet, together with its done and free. So pause/before-execute gave meta before or after the resume, depending on timing.

The reader now parks what follows such a call (begun(), moveToNextResult, endOrPark), as it already did for fetched rows. A paused query emits nothing until it is resumed. This settles the open question in HANDOVER.md.

Not held yet: an error at execute while paused. beginQuery queues the free before it rejects, so holding the error would let free out first.

The explorer

test/contract/explore.js replays each recorded scenario many times over the fake driver. Each result is held for its recorded latency, give or take a fifth, plus a few ms. The scenario's own timers and the JS layer's steps then land at different points against the driver's results. Every run must match the corpus, with no violations. This is how the meta above was found.

  • The native logs now carry timestamps, and the fake holds each result for as long as the driver took. So timer-driven scenarios (pause after 100 ms, cancel after 300 ms) replay with the server's timing.
  • A timed scenario, one paused by a timer, is compared by shape and total rows, because how the rows split around the pause is down to timing.
  • A failing run prints its seed, and --seed n --runs 1 replays exactly that timing.

New scenarios

These are real-server scenarios: a pause landing while the query executes (pause/during-execute-then-result, -then-error), and while a fetch is in flight (pause/during-fetch). SQL Server sends nothing of a batch until a waitfor in it is over, unless what comes before it fills a packet. That is why the first two pause during execute.

pause/before-execute, during-execute-then-result and during-fetch are agreed. during-execute-then-error stays unreviewed, because it still has no done, which is the error-path gap for #479.

Testing (Windows, localdb)

  • Full suite: 992 passing.
  • Replay: 84 passing, 33 s. Slower than before, because real latencies are now replayed, including the 1 s waitfors.
  • Explorer: 83 of 83 scenarios held over 6 timings each, and the 3 timed pause scenarios over 8 timings at 3× jitter.

🤖 Generated with Claude Code

TimelordUK and others added 2 commits October 17, 2026 15:05
…plore timing over the fake driver

A pause that landed while a native call was in flight still let that call's
events out when it returned: the meta after execute or after the next result
set, and an error from a fetch or nextResultSet (with its done and free). So
pause/before-execute gave meta before or after the resume, depending on
timing. The reader now parks what follows such a call (begun(),
moveToNextResult, endOrPark), as it already did for fetched rows, so a paused
query emits nothing until it is resumed. An error at execute while paused is
not held yet: beginQuery queues the free before it rejects.

test/contract/explore.js replays each recorded scenario many times over the
fake driver with jittered timing, so the scenario's timers and the JS layer's
steps land at different points against the driver's results; every run must
match the corpus with no violations. It found the meta above. The native
logs now carry timestamps, and the fake holds each result for as long as the
driver took, so timer-driven scenarios replay with the server's timing.

New scenarios: pause/during-execute-then-{result,error} and pause/during-fetch
(a pause landing while the query executes, or while a fetch is in flight).
pause/before-execute, pause/during-execute-then-result and pause/during-fetch
are agreed. All 83 native logs re-recorded with timing.

Full suite: 992 passing. Replay: 84 passing. Explorer: 83 of 83 held over 6
timings each.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ding on every platform

pause/during-fetch is paused by a timer, so how its rows split around the
pause depends on timing; on the CI runners it came out 950/50 against 800/200
recorded. Only the explorer compared timed scenarios by shape and total rows;
the real-server contract test, the replay and capture.js compared the exact
split. corpus.matchesScenario now does this for all of them.

Every native recording is now replayed on every platform, compared with the
corpus of the platform it was recorded on: the replay tests the JS layer,
which is the same everywhere, and only the driver differs. The Windows
recordings therefore also run on Linux CI, and on macOS, which has no server
and had no behavioural test; the macOS job now runs the replay. The replay
suite also runs the pause, cancel and queue recordings at two fixed seeds of
jittered timing, so a timing regression fails CI and replays with --seed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@TimelordUK
TimelordUK merged commit 8675a3a into master Oct 17, 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