Repository navigation
perf(browser): read a record from peers that may hold it, and fetch the next record after the read - #242
Conversation
…he next record after the read A browser read's first hints are its bootstrap peers, which share few or no leading bits with the record. Its early GETs walked them one at a time, each paying a new connection lane and a round trip, while a holder that discovery had already authenticated waited: 1.25-1.77 s at the median on mainnet. Lookup replies also named peers that cannot hold the record, and their preconnects held the slots a holder needed. - ReadProgress records the peers each lookup reply lists. A peer with a whole close group listed nearer the record cannot hold it: it is dropped from the read's hints, and the read's preconnects skip it. A holder is in the close group by definition, so the rule never excludes one, reachable or not. - A read that continues no other fetches the record after it only once it has finished, so that record's lookup and transfer no longer compete with the one the read waits for, such as a video's head. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Where a warm browser read's first record waited on mainnet, the two changes, their measurements against main, the cost to holder spreading, and what is left: the node-side DTLS delay (saorsa-transport#229), large lookup answers and round gating. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…p preconnects Review fixes for the first-record read: - A lookup reply's peers count toward `ReadProgress::listed` only when they are proven: the responder, which its session authenticated, and peers whose owner-signed address records `find_node` verified. A responder without signed addresses could otherwise list invented identities near the record and rule out its holders. - Preconnects are no longer gated by the read's rule, so a responder that lists signed records for invented identities can at most suppress early reads; discovery and its preconnects are unchanged. - `ReadProgress` reuses the read engine's `may_hold`, takes its lock once per pruning, and notifies the read only when a listing removes a hint. - Tests: signed lookup replies from the mock (`BrowserTestNode::signed_peer`), a regression test for unproven listings, the close group size from Rust (`test_close_group_size`), a test that the listing stays bounded, and a test that a listing that rules out nothing does not wake the read. - CHANGELOG entry, and ADR-0004's decision updated to match. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…view fixes The traced read and try.autonomi.com's page, both against main on 3e9b2bc, replace the earlier revision's measurements. The DTLS note now gives the cause V2-1770 found: the node's ICE-lite agent starts DTLS only once the browser nominates the connection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaidSafe-Hermes
left a comment
There was a problem hiding this comment.
Re-reviewed at f70c39ff08d3caa1792434d7058d32b7bf5521f2.
Both review seats agree (two model families): 2/2 seats reviewed — sol-1 (gpt-6.1-sol xhigh) and glm-1 (z-ai/glm-5.3). The incremental fix now counts only the authenticated lookup responder and peers with verified owner-signed address records; find_node binds each proof to the returned peer ID. Unproven listings cannot rule out early-read hints. The holder filter no longer gates discovery preconnects, and the shared XOR-distance predicate plus send_if_modified preserve the intended cutoff and avoid waking reads when no hint changes. The added browser regressions cover signed listings and unsigned invented identities. I found no material blocker in the re-review.
CI at this head passes the build, unit, Clippy, Browser WASM, real Chromium/local-node, E2E, Merkle E2E, docs, format, and governance checks. Security Audit remains failed; at the requester’s direction, I have excluded that out-of-scope failure from this code verdict. The focused local Cargo test attempt did not complete within the execution limit, so it is not counted as passing evidence.
A browser stream's first frame waits for its first record. On mainnet, this change starts the GET to that record's holder 1.9 s after the read begins instead of 4.3 s in Chromium (sooner in 10 of 10 rounds), and 2.2 s instead of 4.4 s in WebKit (5 of 6). On try.autonomi.com's own page, the median first frame fell from 5.8 to 3.1 s in Chromium (16 of 20) and from 4.0 to 3.1 s in WebKit (5 of 6), and seeking to 85% did not get slower.
What was slow. The read was traced on mainnet: main
7a432e7, 15 Chromium rounds, a warm client reading the first 1 MiB of try.autonomi.com's featured video 3 s after connecting. The holder's GET started at a median 4.1 s:What changes.
ReadProgress::listedrecords the peers each lookup reply lists. A record is stored by its close group, so a peer withCLOSE_GROUP_SIZElisted peers nearer the record cannot hold it.ReadProgress::may_holdis false for such a peer, and it leaves the read's hints.find_nodeverified. A peer listed without a record counts for nothing.ADR-0004 gains a dated section with these measurements. The largest delay left is on the nodes: DTLS takes 1.39 s at the median after ICE connects, because a node's ICE-lite agent starts DTLS only once the browser nominates the connection, which the browser does late. That is V2-1770 (WithAutonomi/saorsa-transport#229).
Linear issue
Closes V2-1769
Risk tier
T1 proposed: there is no wire, format or node change. A browser sends fewer GETs. Reviewers should note:
Compatibility
data::network::ReadProgressgainslistedandmay_hold. ABrowserNetworkadapter that does not calllistedkeeps today's hints.Semver impact
Test evidence
On
3e9b2bc, which is on main27461b2. The review fixes are in that commit.cargo fmt --all -- --check,cargo clippy --all-targets --all-features -- -D warnings, andcargo clippy -p ant-core --target wasm32-unknown-unknown --no-default-features --features browser-wasm[,test-utils] -- -D warnings: clean.cargo test --lib --all: 878 passed. FiveReadProgresstests:Browser bindings (
node --test ./wasm-tests/*.test.mjson a test-utilswasm-packbuild): 254 passed. One earlier full run had a single failure in a read-budget test this PR does not touch; it then passed 5 of 5 alone, 3 of 3 with its file, and in the next full run. Three binding tests are new, and each fails without its behaviour:BrowserTestNode::signed_peer). Without listing, as on main, the read asked 4 far hints, in 3 of 3 runs.scripts/adr-governance.py: passed.Mainnet, traced (2026-10-28, 14:59–15:05 UTC). Test-utils builds of main and the branch, each with the same trace patch, read the first 1 MiB of the 54.9 MB video. Each sample ran in a fresh browser context, with the arms in rotating order. All 32 samples succeeded.
Mainnet, on try.autonomi.com's own page (15:05–15:26 UTC).
cac5363, packaged twice. The copies differ only in the core: main's production WASM (7873c651…, byte-identical to SDK 0.1.4's) and this branch's (eea59cc4…).Earlier revision.
ea1175aalso gated preconnects and counted unproven listings. Through the SDK'screateMediaSource(), it measured:Not run here: real Safari, Firefox, and the native e2e and devnet suites, which CI runs.
CI's Security Audit fails on RUSTSEC-2026-0387 (
crypto-bigint0.5.5, throughelliptic-curve0.13 and alloy). The advisory is from 2026-10-23, and the job has failed the same way on other branches since at least 2026-10-27. This PR changes no dependency; the fix needs an alloy upgrade of its own.New dependency
none
ADR
n/a for T1. ADR-0004 (Proposed) gains the section "First record of a read (2026-10-28)".
Mitigation / rollback
Revert the PR. Both changes are local to the browser client's read path, and native reads are unaffected.
🤖 Generated with Claude Code