Stream reads over HTTP Range requests and from Blobs - #139
Open
OBrezhniev wants to merge 2 commits into
Open
Conversation
readExisting with a URL previously buffered the entire body in memory (browser: any string; Node: unsupported). Now a Range probe (bytes=0-0) routes to a paged read-only backend: large reads stream the 206 body straight into the caller's buffer, small header reads coalesce into cached pages. If-Range with the validator captured at open fails reads if the remote file changes mid-session instead of mixing versions. Servers without range support fall back to the old buffer-it-all path, reusing the probe response body. Blob/File inputs stream via slice(). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wo6AVSAwvL9mHTpvnREZPR
Callers (snarkjs) pass 8 MiB pageSize hints meant for the disk cache; over HTTP that turned a 4-byte header read into a whole-file range request for any file under the page size. Cap http pages at 64 KiB and blob pages at 1 MiB; reads at/above the page size already bypass the cache, so large section reads are unaffected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wo6AVSAwvL9mHTpvnREZPR
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.
Stream reads over HTTP Range requests and from Blobs
Stacked on #138 (
feature/direct_rw_optimization) — this PRshows only the two streaming commits.
Summary
readExistingwith a URL previously buffered the entire response in memory(the browser path fetched any string whole; Node had no URL support at all).
That defeated chunked zkey streaming for browser provers: the full zkey was
resident before the first section read.
Now a URL opens a range-backed read-only fd:
GETwithRange: bytes=0-0. A206gives rangesupport, the total size (from
Content-Range), and a validator (strongETag, elseLast-Modified) in a single round trip. Anything else fallsback to the historical buffer-it-all behavior, reusing the probe's own
response body (no second fetch).
response body directly into the caller's buffer (typed array or BigBuffer)
— no second full-size copy. With snarkjs' chunked section readers, the
zkey's point sections are never resident as a whole.
page-aligned cached ranges (LRU) instead of issuing one HTTP request per
4-byte field. Pages are capped at 64 KiB regardless of the caller's
disk-tuned pageSize hints — snarkjs passes 8 MiB pages, which would
otherwise turn a 4-byte header read into a whole-file range request.
If-Rangewith the validatorcaptured at open. If the remote file changes mid-read, the server answers
200and the read throws instead of silently mixing chunks of two fileversions.
readExisting(blob)streams viablob.slice(pos, pos+len).arrayBuffer()— a browser<input type="file">zkey reads from disk chunk-by-chunk with the same bounded footprint as the
Node file backend (pages capped at 1 MiB).
Both backends share one implementation (
src/rangefile.js), parameterized bya positioned
readRangeInto(dst, dstOffset, pos, len)primitive. Writeoperations throw; the transport is strictly read-only.
Server requirements (CORS deployments)
Rangerequest header; exposeContent-Range,ETag,Accept-Ranges.Content-Encoding(ranges address encoded bytes; zkeycontent is high-entropy anyway). S3/GCS/CloudFront work out of the box.
Validation
npm test: 31 passing, including 10 new tests against local HTTP servers:range streaming with request accounting (no full-file GET ever issued),
single-request page-cache reuse, sequential unpositioned reads, the
no-range-support fallback (exactly one request), mid-session file-change
rejection via If-Range, out-of-bounds/write rejection, string reads through
the page cache, the disk-hint page-size cap, and Blob positioned reads.
Downstream, binfileutils gained URL round-trip tests (header scan + full and
partial section reads in both server modes) and snarkjs an e2e test proving
groth16 with the zkey served by a local range server — proof verifies, every
request is a Range request, and no single response carries the whole zkey.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Wo6AVSAwvL9mHTpvnREZPR