Repository navigation
Let a page be read live as a frame and element slices - #349
Conversation
Convex re-runs a live query in full whenever a row it read is written, so the editor's page:getSnapshot subscription re-reads every row of the page after every edit: 58.5 KB on an 87-element page, measured on a local deployment with dev's data. Add page:getFrame (the page, its settings, its lineups and their images) and page:getElementSlice (the page's elements in one public-id range, with their images, and picture aliases apart), backed by a new elements.by_pageId_and_publicId index. Read as a frame plus slices covering every id, a page holds what page:getSnapshot returns, and an edit re-runs only the slice holding its row: 10.7 KB with 8 slices. Additive only: getSnapshot answers as before (now sharing its helpers), and no client reads the new queries yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 54 minutes. View limit details
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Merge Risk: ⚪ Minimal · up to
|
The client codegen now shares one class between page:getSnapshot and page:getFrame for the page and its content, named after the first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Aliases list a copy's picture under the copy's own id for builds from before pictures had their own id. Every reader of page:getFrame and page:getElementSlice keeps picture ids (and looks pictures up by them), so the read in parts now always sends them and sends no aliases. Rebuilding aliases across slices would also have had to follow chains of them in page order, which independent slices can't. getSnapshot reads its assets before sorting elements and lineups again, as on main, so its answer keeps its order for every input. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The read in parts always carries picture ids and sends no aliases (#349). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bytes re-read after one edit on an 87-element page, by how the live page is read (local deployment loaded with dev's data, same session script):
page:getSnapshot(today)Server half of proposal v2 (read costs). Convex re-runs a live query in full whenever any row it read is written. The editor's
page:getSnapshotsubscription reads every row on the page, so after every edit it re-reads the whole page. In a heavy day that's 83% of everything a user reads from Convex, and it counts against our database I/O cap.What this adds. Additive only, no client reads it yet:
page:getFrame: the page, its settings, its lineups and the images they show.page:getElementSlice(fromPublicId, toPublicId?): the page's elements whose public id is in[from, to), with the images they show.pictureId(assetId ?? id), so they'd only be dead weight. Rebuilding them across slices would also have meant following alias chains in page order, which independent slices can't do (Astra round 1 found that case).elements.by_pageId_and_publicId.Each part is an ordinary complete query over a fixed range. A row that moves to another page, or is deleted, restored or purged, just leaves or enters its slice, and Convex re-runs that slice. There are no change numbers and nothing to keep in step across write paths. That's why this replaced the per-row change-number design: Astra's review of that one found six ways it could show stale work.
page:getSnapshotanswers exactly as before for every shipped desktop build and the live web build. It now sharesreadablePage/pageContentOf/viewerAssetswith the new queries, and still reads its assets before sorting elements and lineups, so even the order of ids that compare equal is unchanged.Order. Rebuilt from parts, elements with the same
sortIndexare ordered by public id, which is how the offline copy already orders them (cloud_strategy_copy_box.dart).getSnapshotorders those ties by creation time. The app change (next PR) will use the copy's order.Checked
pageSlices.test.ts: a page rebuilt from frame + slices equalsgetSnapshot(acceptsPictureIds: true)less its aliases (in place order, ties by code unit as Dart orders them) for 1, 2, 4 and 16 slices, on both pages, before and after a move to the other page, a delete and a restore onto the other page. The page has images, copies sharing the original's picture inside and outside the original's slice, a lineup with an image, ids outside the hex range and an id exactly on a slice boundary. Mutation-checked: an inclusive upper bound fails it.E:\w\convex-io\harness,session-slices.mjs --slices 8|16) against this branch's code: the table above. Byte counts on the local backend match Convex cloud exactly.npx tsc --noEmit, fullnpm run test:convex(329 tests), contract snapshot refreshed against a local deployment of this branch,npm run audit:convex-contract(60 functions), Dart client regenerated (only the two new queries change; the generator now shares one class for the page and its content betweengetSnapshotandgetFrame, soconvex_strategy_repository.dartfollows the new names);flutter analyze lib testhas no errors.Not in this PR: the app reading pages this way (it touches the sync path, so you look first), and the offline-copy skip (separate design).
ci: test convex contract
🤖 Generated with Claude Code