Skip to content

perf(server): batch hydrateRows; per-table catch-up reads use the (tbl,seq) index#8

Closed
grrowl wants to merge 1 commit into
advisor/001-server-error-path-testsfrom
advisor/004-catchup-sql-batching
Closed

perf(server): batch hydrateRows; per-table catch-up reads use the (tbl,seq) index#8
grrowl wants to merge 1 commit into
advisor/001-server-error-path-testsfrom
advisor/004-catchup-sql-batching

Conversation

@grrowl

@grrowl grrowl commented Jun 11, 2026

Copy link
Copy Markdown
Owner

Executes plan 004 from the advisor audit. Stacked on #4 (plan 001) — merge that first, then retarget/merge this; the diff vs its base is src/server/changes.ts + the emitCatchUp call site + two new tests.

What

  • hydrateRows: N+1 (one SELECT … WHERE pk = ? LIMIT 1 per changed key) → chunked IN queries (CHUNK=64, quoted identifiers, bound values, String(row[pk]) keying preserved per ADR-0007/D9).
  • New readChangesSinceFor(sql, tbl, cursor): per-table changelog read that actually uses the _sync_changes_tbl_seq index (whose comment always claimed it "serves per-table catch-up" — now true). emitCatchUp uses it; drainAndBroadcast's deliberate one-scan-all-tables read is untouched.

Why

Cuts SQLite rows-read (billed) and DO wall-clock exactly where reconnect storms concentrate: a 500-key catch-up was 500 queries over a full-log scan; now ~8 queries over an index range.

Verification

Two new falsifiable tests in tests/cdc.test.ts (3-chunk hydration with missing keys; table isolation + mid-stream cursor windowing); catchup/reconnect/multiplex wire tests green; full suite green on the reviewer's re-run.

🤖 Generated with Claude Code

…l,seq) index

Eliminates the N+1 SELECT in hydrateRows by issuing chunked IN queries
(64 keys per chunk), cutting SQLite rows-read on reconnect storms.

Adds readChangesSinceFor for emitCatchUp's per-table read, activating the
_sync_changes_tbl_seq composite index that previously went unused — the
JS .filter() after readChangesSince is gone. drainAndBroadcast's all-tables
readChangesSince call is intentionally unchanged (one scan, N tables).

New tests in cdc.test.ts pin: chunking over 150 rows (no drops/dups, TEXT pk
keying), and readChangesSinceFor table isolation + mid-stream cursor windowing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@grrowl

grrowl commented Jun 13, 2026

Copy link
Copy Markdown
Owner Author

Superseded by #14, which landed plan-004 on main (adapted for main + published @tanstack/db, validated green) as part of the 0.3.2 bug-fix batch.

Not merging into feat/ssr: feat/ssr will receive this via its rebase onto the new main, so merging here would duplicate/conflict with that. Closing as superseded.

The advisor/* branch is retained (not deleted) until the feat/ssr reconciliation is done — it's the source for restoring any feat/ssr-specific bits (e.g. the readSyncSnapshot test case, the extracted scheduleReconnect method) that main's adapted versions intentionally omit.

@grrowl grrowl closed this Jun 13, 2026
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