Repository navigation
perf: answer entry listings from entry_list_values (#110) - #134
Merged
Merged
Conversation
…#110) RFC-0042 §4's side table: entry_list_values holds the indexed list-field values of entries (field, presence, number, text, collection-sort key, creation time, node, locale, status) with a covering query index and a per-node index; entry_list_state holds each listed collection's readiness (definition fingerprint, ready flag, backfill epoch and cursor). DDL only; plain SQL on SQLite, D1 and Durable Object SQLite. Refs #110 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Madraka <8898640+Madraka@users.noreply.github.com>
queryContentEntries (GET /entries?where=…, Studio lists, MCP list_entries) now answers where, orderBy, status, locale, limit and offset in SQL from the side table once a collection is ready, instead of reading and evaluating the whole collection. The shared evaluator stays the fallback (term filters, collections not ready, operands SQL cannot compare exactly) and the oracle of a seeded property suite on SQLite, D1 and DO SQLite. The store keeps the table exact: entry writes write their rows (the listFields values resolveFields reads) in their own batch, an original's write rewrites its translations' rows, and site writes, field operations and imports that change list definitions reset readiness in the same batch; a race marks a collection not ready instead of refusing a write. rebuildListValues is the resumable, budgeted, concurrency-safe backfill; updateSite, finished field operations, importSite and openLocalStore run it for what they reset. revertTagsToTerms now leaves an entry edited after its before-snapshot as it is and reports it. 10,000-entry benchmark, store path: SQLite 126 ms / 10,001 rows -> 0.6 ms / 22; D1 212 ms / 10,003 rows read -> 4.7 ms / 5,068. MCP list_entries with orderBy and limit lets the store page. RFC-0042 and ADR-0044 carry the implementation note and known limits. Refs #110 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Madraka <8898640+Madraka@users.noreply.github.com>
The admin Worker's tick gains a lists.backfill job every five minutes: a budgeted rebuildListValues for listed collections that are new, changed, reset by a race or older than migration 0029. Its declared cost (16 statements free, 150 paid) is the statement budget the backfill enforces; each run starts at another waiting collection, and on paid it tidies rows of collections that no longer list fields. Closes #110 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Madraka <8898640+Madraka@users.noreply.github.com>
Madraka
enabled auto-merge
October 26, 2026 07:31
A seed builds a site, mutates and reshapes it three times and checks 150 queries on each backend: about 5 s locally, over 30 s (the package's test timeout) on a 4-vCPU runner with other packages testing, where seeds 11, 37 and 41 timed out on #134. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Madraka <8898640+Madraka@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Madraka <8898640+Madraka@users.noreply.github.com>
setup.ts's beforeAll compiles every language's catalogs and lazy namespaces. With core-ts's list-value suites testing alongside, it passed Vitest's default 10 s hook timeout on CI twice (design-rules.test.ts on #134); tests already get 20 s for the same reason. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Madraka <8898640+Madraka@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Closes #110 (RFC-0042 §4, the side table; open question 10's trigger fired in #105's benchmark).
queryContentEntriesservesGET /entries?where=…, Studio's lists and MCPlist_entries. It now answerswhere,orderBy, status, locale,limitandoffsetin SQL from a newentry_list_valuestable once a collection is ready. Until now it read and evaluated the whole collection on every call.a95b7113)The bench (
pnpm --filter @cloudhal/astro-renderer bench:lists) asserts the RFC's threshold (≤ 50 ms CPU, ≤ 10,000 rows) for the API path and the broad unordered page.How
0029addsentry_list_valuesandentry_list_state.entry_list_valuesholds field, presence, number, text, sort tie-break, created, node, locale and status, with a covering query index and a per-node index.entry_list_stateholds each listed collection's fingerprint, readiness, backfill epoch and cursor. The migration is DDL only.src/list-values/):resolveFields, forlistFieldskeys only (at most 4 per collection). Every entry gets exactly one row per listed field, plus one row per chosen option for multi-choice fields. Long text and doubles SQL can't compare exactly are leftNULL, and those queries fall back to the scan.rebuildListValuesis resumable and budgeted, and safe to run concurrently because every chunk is guarded on fingerprint, epoch and cursor.updateSite, finished field operations,importSiteandopenLocalStorerun it for whatever they reset.lists.backfillsystem job runs every five minutes. Its declared cost (16 statements on free, 150 on paid) is enforced as a statement budget on every statement. Each run starts at another waiting collection. On paid it also tidies rows of collections that are no longer listed, never those of a collection listed while it ran.list_entrieswithorderByandlimitlets the store page. Arguments, cursors and output are unchanged.revertTagsToTermsnow leaves an entry edited after its before-snapshot as it is and reports it inedited. Before, it restored the entry's earlier fields, status or collection.Known limits (written up in RFC-0042)
whereand noorderBy(MCPlist_entries) returns every matching body. For 5,000 matches that is about 15,000 rows read on D1 (71 ms CPU), still over the threshold. Per-collection counters or paging would fix it.totalis the whole collection (orderBy alone,ne,unset) still counts about N index rows, though with far less CPU.Verification (local, one process at a time)
test/list-values-sql.test.tsuses EXPLAIN QUERY PLAN to check that no write-path or backfill statement scansnodes, and that the query statements seek their indexes.listings-api/fields-api/collections-apion sqlite and D1, and cloudflarelist-values-job(D1: 4 sites and 20 collections never exceed the job's cost) all pass.openLocalStorenow backfills.pnpm typecheck(20/20),pnpm lint,pnpm size:check(store.tsshrinks 1221 → 1212),pnpm driftand the MCP tools doc check all pass.Checklist
pnpm checkandcargo test --workspacepass locally. Everything touched was run, including the full core-ts suite. There is no Rust or contract change; CI runs the rest.git commit -s, DCO)Closes #110
🤖 Generated with Claude Code