Skip to content

perf: cut the fixed per-change cost of a watched batch reconcile - #1564

Merged
thekevinbot merged 4 commits into
mainfrom
bot/1562-watch-speed-1102
Nov 3, 2026
Merged

thekevinbot merged 4 commits into
mainfrom
bot/1562-watch-speed-1102

Conversation

@thekevinbot

@thekevinbot thekevinbot commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

Refs #1562

Root cause

A real fixed per-change overhead, plus a noisy 15-pair measurement. It is not a recent regression: commits back to 2026-10-14 fail the same bar on this host.

  • On main, 300-pair raw runs: native median 388us, dirsql 435-439us, ratio 1.12-1.14 across 3 runs. That is about 48us of fixed cost per change.
  • Tick breakdown of one change on main, in us after the write: hook spawn 36, cat EOF 225, child reaped 247, reconcile done 313, consumer has the event 337.
    • The reconcile took ~56us live. row_key alone took ~17us, because it ran three pragma queries on every rerun.
    • The range refit (DELETE, min/max query, INSERT) took ~14us.

Change (packages/rust/src/db.rs)

  • A table's key candidates are cached per PRAGMA schema_version, so any schema change invalidates them.
  • get_table_columns uses a cached pragma_table_info(?1) select.
  • The range refit is skipped when every change is an in-place update.
  • Otherwise the range is fitted from the rowids the reconcile placed, with no min/max query.

After the change, reconcile is ~18-21us live (row_key is ~0.5us).

Measurements (same host, interleaved runs, bar = 1.1x, lower is better)

main this branch
e2e passes, round 1 1/8 5/8
e2e passes, round 2 3/10 7/10
e2e passes, round 3 6/10 7/10
typical bar median ~1.13 ~1.07
side-by-side A/B, both orders, 400 pairs x 3 branch/main ~0.95

The test is still not reliably green on a loaded host: the same binary ranges 0.95-1.34 from run to run. The remaining ~20-25us gap is structural:

  • waiting for the hook child to exit after its stdout EOF: ~21us, which is cat's own exit cost
  • two thread/task hops before the SSE write: ~9us

Things tried with no measurable gain, reverted: prepare_cached for the transaction statements, translating watcher events on the reading thread, reaping the child on the stderr thread (worse), and spinning on try_wait after EOF.

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)

E2E Verification

  • Ran e2e suites locally for every affected SDK
  • Python SDK e2e: pass
  • TypeScript SDK e2e: pass
  • Rust core e2e (if applicable): N/A (cargo test --lib: 1299 passed)
  • 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:
    • testing-conventions e2e attest 'uv run python -m pytest tests/e2e/on_file_query_test.py tests/e2e/query_contract_test.py tests/e2e/declared_table_name_test.py tests/e2e/fanout_test.py tests/e2e/use_case_dossier_test.py tests/e2e/use_case_join_test.py tests/e2e/use_case_notebook_test.py tests/e2e/use_case_transcripts_test.py tests/e2e/watch_speed_test.py -q'
    • just e2e-attest-ts
  • Result summary:
    • Python: 22 passed. watch_speed_test passed on that run, but it passes only ~70% of runs on this host (see above).
    • TypeScript: 18 passed.

Read a table's key candidates once per schema version, read its columns
through a cached statement, skip the range refit when every change is an
in-place update, and fit the range from the rowids the reconcile placed
rather than a min/max query.
@thekevinbot
thekevinbot marked this pull request as ready for review November 3, 2026 01:34
@thekevinbot
thekevinbot merged commit 05880bb into main Nov 3, 2026
89 checks passed
@thekevinbot
thekevinbot deleted the bot/1562-watch-speed-1102 branch November 3, 2026 01:34
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