Skip to content

perf(browser): ask lookup nodes for a close group in a read's lookups - #243

Draft
mickvandijke wants to merge 3 commits into
mainfrom
mickvandijke/v2-1779-read-lookup-answers
Draft

mickvandijke wants to merge 3 commits into
mainfrom
mickvandijke/v2-1779-read-lookup-answers

Conversation

@mickvandijke

Copy link
Copy Markdown
Member

A browser read's lookups now ask each node for its record's close group, count.max(CLOSE_GROUP_SIZE), which is 7 nodes for a default read, instead of a 20-node witnessed view. On mainnet a 7-node answer took 4.1 round trips on a fresh connection, against 5.3 for 20 nodes, and 1.0 on a used connection against 3.1. The holder of a stream's first record is named ~0.25 s sooner in both engines. End to end, WebKit streams start and seek sooner, but Chromium streams did not start sooner in these runs (details below), so this is a draft.

Why answers were slow. Every node a FIND_NODE answer lists carries its owner-signed record, an ML-DSA-65 key and signature of about 6 KB, so a 20-node answer is about 115 KB. The node's SCTP send window starts at 4,380 bytes (webrtc-sctp's fixed initial window) and doubles each round trip. A read's lookups only need the record's close group, but they asked for the 20-node view that quotes cross-check.

What changes.

  • BrowserNetworkLookupQuery::query_batch asks for count.max(CLOSE_GROUP_SIZE) when the lookup serves a read (it has a ReadProgress), and count.max(SINGLE_NODE_WITNESSED_VIEW_COUNT) otherwise, as before.
  • Only find_read_peers, the browser's chunk reads (streams, downloads, DataMap fetches), takes the smaller size. These keep 20:
    • quotes and uploads, through find_witnessed_close_group, whose views are cross-checked against nodes' 20-node acceptance window;
    • PUT targets, Merkle payments and pointers, through find_closest_peers.
  • Native reads are unchanged.
  • ADR-0004 gains the dated section "Lookup answers for reads (2026-10-29)".
  • The changelog gains this entry. It also moves perf(browser): read a record from peers that may hold it, and fetch the next record after the read #242's entry to [Unreleased]: its merge after the changelog's rotation (docs(changelog): rotate [Unreleased] and backfill 0.3.7 to 0.3.12 #238) had put it under 0.3.8.

Linear issue

Closes V2-1779

Risk tier

  • T0 — docs / tooling / CI / pure UX-output. Repo CI only.
  • T1 — client-only, no network-facing behavior change. CI + prod compat smoke.
  • T2 — node/client logic with behavioral surface, no protocol/format/economics change. Dev testnet + ADR.
  • T3 — protocol / storage format / payments / routing. T2 evidence + adversarial testing.

T1 proposed: the same FIND_NODE request with a smaller count, which nodes already accept (they clamp it to 1–20). There is no node, wire-format or payment change. Reviewers should note:

Compatibility

  • Wire: none. Read lookups send FIND_NODE with count: 7 instead of 20.
  • Storage: none.
  • API: none. The test-utils bindings gain BrowserTestNode::last_find_node_count and test_witnessed_view_count.

Semver impact

  • breaking
  • feature
  • fix

Test evidence

On 612a98b, which is on main 458c717.

  • cargo fmt --all -- --check, cargo clippy --all-targets --all-features -- -D warnings, and cargo 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.

  • Browser bindings: 256 passed. The mock node now records the count each FIND_NODE asks for. New tests:

    • a read's lookups ask each node for its record's close group. On a build that asks for the full view it fails, with counts of 20 instead of 7.
    • an upload's lookups still ask each node for a full witnessed view. It passes on both.
    • peers a lookup reply lists without an owner proof do not rule out a holder, a perf(browser): read a record from peers that may hold it, and fetch the next record after the read #242 test, now names the holder through a second bootstrap node, since an answer of a close group of invented identities leaves no room for it. It still fails, 2 of 2 runs, on a build that counts unproven peers.
  • scripts/adr-governance.py: passed.

  • Mainnet, per answer (2026-10-29, 16:12 UTC). Each sample authenticates one of the 7 mainnet seeds, idles 3 s, then asks for 7 and 20 nodes 3 s apart, in alternating order. Median round trip 167 ms; 14 samples, all succeeded.

    answer 20 nodes (115 KB) 7 nodes (42 KB)
    first on a connection 1,091 ms, 5.3 round trips 628 ms, 4.1 round trips
    later, on a used connection 488 ms, 3.1 round trips 184 ms, 1.0 round trip
  • Mainnet, traced first read (16:15–16:20 UTC). Test-utils builds of main and the branch, each with the same trace patch, read the first 1 MiB of try.autonomi.com's 54.9 MB video. All 32 samples succeeded.

    Chromium (10 rounds) WebKit (6 rounds)
    holder first named 0.93 → 0.66 s 0.87 → 0.63 s
    holder's GET 1.73 → 1.57 s, sooner 6/10 2.0 → 1.6 s, sooner 6/6
    read done 3.1 → 5.0 s, sooner 3/10 2.7 → 2.3 s, sooner 6/6

    In Chromium the later reads came from the one holder's transfers. Those took 0.6–4.9 s in both arms, and twice the holder stalled past the 3 s hedge.

  • Mainnet, streaming on try.autonomi.com's page (16:20–16:45 UTC).

    • Arms: try cac5363's page with SDK 0.1.5's client, against the same page with this branch's production WASM (6ae86570…).
    • Each sample: clicks Stream on the LEGO video 3 s after connecting, times it to the first decoded frame, plays it for 10 s, then seeks to 85%.
    • Results: all 52 samples played, with no page errors.
    Chromium (20 rounds, two batches) WebKit (6 rounds)
    first frame 5.0 → 5.8 s, sooner 8/20 4.4 → 4.0 s, sooner 4/6
    seek to 85% 5.6 → 5.1 s, sooner 10/20 5.2 → 2.8 s, sooner 6/6
    data received per sample 74.3 → 63.8 MB 67.7 → 55.0 MB
    • Chromium first frames were a median 1.0 s later, paired by round. The first batch was 3/10 sooner, the second 5/10.
    • Mainnet was slow in these batches: the baseline's median first frame was 5.0 s, against 2.4–3.9 s in other runs, and the one holder's transfers dominated.
    • This is not yet a measured gain for Chromium streams. It needs more rounds, or a latency-emulated devnet run, before merging.
  • Not run here: real Safari, Firefox, and the native e2e and devnet suites, which CI runs.

Scripts and raw results: ant-testnet/state/comparisons/v2-1769-lookup-answer-size-1029/ (local).

New dependency

none

ADR

n/a for T1. ADR-0004 (Proposed) gains the section "Lookup answers for reads (2026-10-29)".

Mitigation / rollback

Revert the PR. It changes only the count a browser read's lookups ask for.

🤖 Generated with Claude Code

mickvandijke and others added 3 commits October 29, 2026 17:10
A browser read's lookups asked each node for a 20-node witnessed view, which
only quotes and uploads cross-check. Each listed node carries its owner-signed
record, an ML-DSA-65 key and signature of about 6 KB, and a node's first answer
on a connection grows from webrtc-sctp's 4,380-byte initial send window, so a
20-node answer (about 121 KB) took 5-6 round trips on mainnet. A read needs each
node's view of its record's close group: its lookups now ask for
count.max(CLOSE_GROUP_SIZE) nodes. Lookups for quotes, uploads, PUT targets,
Merkle payments and pointers still ask for the full view.

The mock node records the count each FIND_NODE asks for. New binding tests:
a read's lookups ask for a close group, and an upload's lookups still ask for
the full witnessed view. The test that unproven listings do not rule out a
holder now names the holder through a second bootstrap node, since a reply of
a close group of invented identities leaves no room for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ngelog

ADR-0004 gains a dated section with the per-answer, traced-read and try-page
measurements, including that Chromium streams did not start sooner. The
changelog entry for this change goes under [Unreleased], and #242's entry moves
there too: its merge after the changelog's rotation put it under 0.3.8.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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