Skip to content

Charge a contract page for the values it maps, not the whole record per field - #1495

Merged
callumalpass merged 3 commits into
mainfrom
fix/contract-projection-charge
Nov 13, 2026
Merged

callumalpass merged 3 commits into
mainfrom
fix/contract-projection-charge

Conversation

@callumalpass

@callumalpass callumalpass commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

Problem

A contract query (query_contract_v1) refused a page with too_large / contract_projection_budget once its projection charge passed 1 MiB. The charge was each record's whole effective frontmatter multiplied by its number of mapped fields.

That refuses ordinary collections:

  • A page of 128 library sources with about 1.5 KB of frontmatter and 14 mapped fields was charged about 2.7 MB, so every page was refused. What the projection copies for that page is under 0.2 MB.
  • Any single record over about 80 KB (1 MiB divided by the number of mapped fields) was refused at every page size, so no client could page around it.

Writer showed "The native collection operation could not complete (too_large)" and Reader showed "Contract projection exceeds its page budget" for a Reader library of this shape. #1286 fixed the same symptom for ordinary record pages; contract pages have had this separate charge since #734.

Change

The charge is still taken before Core copies anything, but it is now what the projection copies: the value each mapping selects, measured once per record field however many contract fields map it. Unmapped fields are never copied and cost nothing.

Unchanged: the 1 MiB budget, the 128-mapping limits, and the response-size cap on the encoded page.

Tests

  • contract_query_projection_budget_is_charged_before_wide_mapping_clones now maps one 12 KB value 128 times (1.5 MB of real copies) and is still refused before the copy. It previously mapped a boolean 128 times beside a 12 KB unmapped field, which is the over-count this PR removes.
  • New contract_query_projection_budget_charges_mapped_values_not_the_whole_record: 60 records, 14 mappings, 4 KB of unmapped frontmatter each. It fails on the old charge and passes now.
  • cargo test -p mdbn-replica passes (1224 tests); cargo clippy -p mdbn-replica --all-targets -- -D warnings is clean.

Runtime pin

The second commit repins the SDK app runtime, built from the first commit with cargo xtask wasm-app under the workflow's path remapping, then node scripts/sdk-publication.mjs pin-runtime. The WASM grows by 1,884 bytes to 3,877,699.

Follow-up not in this PR

A page whose real projection exceeds the budget is still refused whole rather than ended early with a cursor. mdbase-writer #98 makes Writer ask again for smaller pages; Reader does not yet.

…er field

A contract query refused a page with too_large / contract_projection_budget
when the projection charge passed 1 MiB, and the charge was each record's
whole effective frontmatter multiplied by its number of mapped fields. A
page of 128 ordinary library sources (about 1.5 KB of frontmatter, 14
mapped fields) was charged about 2.7 MB, so every page was refused, and
any one record over about 80 KB was refused at any page size. Writer and
Reader could not load a Reader library of that shape.

The charge is still taken before Core copies anything, but it is now what
the projection copies: the value each mapping selects, measured once per
record field. Unmapped fields are never copied and cost nothing. The
budget, the limits on mappings and the response-size cap are unchanged.
@callumalpass
callumalpass force-pushed the fix/contract-projection-charge branch from c51f023 to ab250aa Compare November 13, 2026 04:29
@callumalpass
callumalpass added this pull request to the merge queue Nov 13, 2026
Merged via the queue into main with commit 4380780 Nov 13, 2026
46 checks passed
@callumalpass
callumalpass deleted the fix/contract-projection-charge branch November 13, 2026 04:58
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