Skip to content

Keep queries out of an open watch batch - #1637

Merged
thekevinbot merged 4 commits into
mainfrom
bot/1616-watch-batch-isolation-1104
Nov 5, 2026
Merged

thekevinbot merged 4 commits into
mainfrom
bot/1616-watch-batch-isolation-1104

Conversation

@thekevinbot

@thekevinbot thekevinbot commented Nov 4, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #1616

A query that skips its own read of the disk (the TypeScript binding's queries, or any core caller with fresh reads off) took only the DB mutex, which a watch batch releases between statements while its transaction is open. The query then ran inside that transaction and could see one table updated and another not, or rows a foreign-key failure later rolled back. Fix: every query holds the index-still lock that watch batches and reconciles take, whether or not it reads the disk first (DirSQL::refresh in packages/rust/src/lib.rs).

The CLI server's queries already wait on that lock since #1577 (they reconcile first), so the issue's server repros pass on main; watch_batch_isolation_e2e.rs keeps them as a regression guard. The red proof is watch_batch_isolation.rs, which queries mid-batch with fresh reads off.

Red run (test commit 67c2a59): https://github.com/thekevinscott/dirsql/actions/runs/41916391931 -- Integration tests and Windows tests fail on exactly the new assertions: torn read: [{"na": Integer(2), "nb": Integer(1)}] and read rows the batch rolled back ([c1] vs [c1, orphan]).

Hand check with the release binary: the issue's torn repro answers {"na":2,"nb":2} 25/25; the dirty repro never returns c2 (the query reports the foreign-key violation instead, from its own read of the disk).

E2E Verification

  • Ran e2e suites locally for every affected SDK
  • Python SDK e2e: pass
  • TypeScript SDK e2e: pass
  • Rust core e2e (if applicable): pass
  • packages/python/e2e-attestations/<branch>.json receipt written if packages/python changed (just e2e-attest-python)
  • packages/ts/e2e-attestations/<branch>.json receipt written if packages/ts changed (just e2e-attest-ts)
  • Command(s) run:
    • cargo test -p dirsql --features cli --tests --no-fail-fast (all pass)
    • python: testing-conventions e2e attest 'uv run python -m pytest tests/e2e/fresh_declared_repros_test.py tests/e2e/query_contract_test.py tests/e2e/on_file_query_test.py tests/e2e/watch_speed_test.py tests/e2e/server_speed_test.py -x -q'
    • ts: testing-conventions e2e attest 'pnpm exec vitest run --dir tests/e2e fresh-declared-repros query-contract fanout'
  • Result summary: Python 20 passed; TS 17 passed; Rust suite green.

Changelog / Migrations

  • Changelog fragment added under <root>/<pkg>/changelog.d/ for each changed package (or: skip-changelog trailer on a commit with reason)
  • Migration fragment with the same slug added under <root>/<pkg>/migrations.d/ for each changed package (every subsection but Summary _None._ when nothing breaks)

@thekevinbot
thekevinbot merged commit b3b1f88 into main Nov 5, 2026
89 checks passed
@thekevinbot
thekevinbot deleted the bot/1616-watch-batch-isolation-1104 branch November 5, 2026 01:24
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.

Keep queries out of an open watch batch: no torn or rolled-back reads

1 participant