Repository navigation
feat(storage): answer get-or-closer requests (ADR-0020) - #249
mickvandijke wants to merge 9 commits into
Conversation
Kademlia's FIND_VALUE for chunks. A new get-or-closer request is answered with the chunk by a holder, and otherwise with the closer peers its FIND_NODE answer would hold, encoded and checked by saorsa-core. Native clients run their own iterative lookup with it. Peers that do not announce `get-or-closer/1` in their user agent get FIND_NODE plus a GET instead, so a new client reads at full speed from today's nodes. When the lookup finds nothing, the client falls back to today's read path, so the absence rule is unchanged. Measured on production: native p50 5.95 s to about 1 s, the whole file 3.7 times faster, at 2.5-3.3 chunk copies per read. Part of V2-1358. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ADR-0020. A node that holds the requested chunk answers `Found` with it. Otherwise it answers `Closer` with saorsa-core's encoding of the FIND_NODE answer it would give the authenticated requester, so clients check those peers exactly as they check FIND_NODE answers. A node with storage enabled announces `get-or-closer/1` in its user agent; one with storage off never answers chunk requests, so it does not. Browsers are not offered the request yet. Get-or-closer traffic gets its own rx and tx rows in the chunk traffic summary (group 4), and each answer is logged as `get_or_closer_rpc`. Builds against sibling saorsa-core and ant-protocol checkouts on the V2-1358 branch until those are released. Part of V2-1358. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A client built from the design read a 612 MB production file in 56 s, against 242 s for the current client, through the per-peer fallback that today's nodes take. In-process networks of upgraded nodes are read with get-or-closer requests only. Part of V2-1358. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replaces the sibling-checkout paths with the pushed revisions, so the branch builds anywhere. Part of V2-1358. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Takes the rc-2026.10.1 release baseline: ant-protocol 4.0.0, saorsa-core 0.29.0 and saorsa-transport 0.38.0. Main no longer patches saorsa-core or saorsa-transport for copy-free sends, so the patch section now pins only the ADR-0020 branches of saorsa-core and ant-protocol, both merged onto that baseline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dirvine
left a comment
There was a problem hiding this comment.
Code approval at the reviewed head
No introduced blocking runtime defect found in this PR. Production responses use the size-capped encoder; authenticated requester identity is passed into the handler. Decoder use here is test-only.
Stack coordination remains required: saorsa-core#173 has a P2 decoder-size blocker. Refresh the core patch pin at Cargo.toml:240 after that fix and rerun integration tests; propagate the resulting node head into the client. This code approval is not approval to ship the current linked dependency set or a waiver of merge/release gates.
Reviewed commit: 79385bfddc04a26b5c0747b3355364c12b9a127b.
Review method: six substantive seats across the linked set, including a completed z-ai/glm-5.2 API review recovered from a worker that timed out after saving its response; coordinator source checks and local execution. Initial functional/security/test seats recommended approval. The coordinator probe, independent adjudicator, API seat and replacement concurrency/release seats corroborate the missing decoder bound. This is not unanimous approval. The API seat recommended client approval with concerns; the coordinator withholds it because the exact client pin still exposes the defect.
Local verification (macOS, rustc 1.96.0): core closer-peers 3 passed and DHT-manager regression 143 passed; protocol all-features 108 passed; node get-or-closer unit tests 3 passed and real-transport E2E 1 passed; client kad_read unit tests 10 passed and real-transport E2E 1 passed. Formatting passed in all four repositories. No production/testnet deployment or merge performed. Draft git pins were verified with cargo metadata --locked; this is not published dependency closure. ADR-0020 remains Proposed.
saorsa-core's decode_closer_peers now rejects payloads over the 64 KiB FIND_NODE limit before deserializing them. This node only encodes closer peers, and its encoder already kept within that limit, so its own behaviour is unchanged. The pin moves so that the client, which pins this node, takes the bounded decoder. Part of V2-1358. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From review of the ADR-0020 stack: - decode_closer_peers rejects a payload over the 64 KiB FIND_NODE limit before parsing it (saorsa-core fd37775). The chunk message carrying it may be up to 5 MiB. - A peer whose content fails verification only drops out of the client's lookup (ant-client 7d9a248). Every queried peer is asked for the chunk, so ending the read on one bad answer would let any dishonest node on a lookup path fail reads. Today's read path, which runs when the lookup finds nothing, keeps its rule that bad content ends the read. - The trade-offs now list that every queried peer can send content. - Validation now covers both changes. Part of V2-1358. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks.
|
Browsers keep today's read. The browser variant was built and A/B'd on a 60-node testnet, and was not faster: a warm browser already holds sessions with the chunk's close group there. The review trigger now names when to revisit it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
930f90e to
9775920
Compare
dirvine
left a comment
There was a problem hiding this comment.
Re-review — pin update confirmed, one coordination item
Fix verified
- saorsa-core pin now
fd377754(the bounded closer-peer decoder) in both Cargo.toml and Cargo.lock — the first-round coordination requirement is met. - ADR-0020 docs updated to match: oversized closer-peer payloads rejected before parsing; bad-content peers dropped.
Coordination item (not a code defect)
- The PR head is
97759205, but ant-client#221 pins ant-node atad886472— a commit inside this PR's branch, not the head. The client consumes the get-or-closer handler and the fixed core pin from that mid-branch commit. - Please either: (a) pin the head after squash/merge, or (b) confirm
ad886472is the intended node revision. Otherwise the client's node dependency tracks an unreferenced commit once this branch merges.
Local verification
- Handler + e2e tests at head
97759205: 3/3 and 1/1 ok (local run, macOS).
Verdict
APPROVE at head 97759205, subject to the node-revision pin being reconciled before the client ships. Panel: GLM 5.3 seat billing-failed (402) — 2/3 reviewed, provisional.
…chunk-get main dropped the migration signal and its user agent token (#218), so the get-or-closer token moves to main's `user_agent` in node.rs, still only on a node that serves chunks. ADR-0020 no longer mentions the migration token. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ADR-0020: a chunk GET returns the chunk, or closer peers, in one request, as Kademlia's FIND_VALUE does. This PR adds the ADR and the node side.
Why. On production, a native chunk read spends 82% of its time finding a holder, because the closest-peers lookup runs to convergence before the GETs start. Asking each queried peer for the chunk as well as for closer peers cuts native p50 from 5.95 s to about 1 s; see the investigation.
Node.
GetOrCloserRequestwithFound.Closer, with saorsa-core's encoding of the FIND_NODE answer it would give the authenticated requester.get-or-closer/1in its user agent. One with storage off never answers chunk requests, so it does not announce it.get_or_closer_rpc.Compatibility by design.
Stack, merge in this order:
This PR pins saorsa-core and ant-protocol at those branches by git rev.
Linear issue
Closes V2-1358
Risk tier
Compatibility
GetOrCloserRequest, and its user agent gains the tokenget-or-closer/1. Thenode/prefix is kept. Every existing message is unchanged.ChunkRequestContext::with_requesterandnode::user_agent(serves_chunks)are crate-private.Semver impact
Test evidence
cargo test --lib --features test-utils: 1,168 passed, after merging main's LMDB store removal (refactor(storage)!: remove the LMDB chunk store, and never refuse a start over what it left behind #218). New tests:node/prefixcargo test --test e2e --features test-utils test_get_or_closer_returns_chunk_or_closer_peers: passed. On a local test network, the holder answersFound, and a non-holder answersCloserwhose peers the client decodes as a FIND_NODE answer, without the requester.cargo clippy --all-features -- -D clippy::panic -D clippy::unwrap_used -D clippy::expect_used -D warnings: clean.cargo +1.99.0 clippy --all-targets --all-features -- -D warnings, as CI runs it: clean.Live. A client from the ant-client PR downloaded a 612 MB production file in 56 s, against 242 s for the current client, with identical bytes. All 150 chunks came through the new lookup, on the route for nodes without the token.
Dev testnet comparison against main, matched (2026-10-09, native clients). Two 60-node fleets on the same layout:
ant-testnet/state/comparisons/v2-1358-kad-vs-main-matched-60m-1009/README.md.New dependency
none
ADR
ADR-0020: Kademlia-style chunk GET: return the chunk or closer peers, added in this PR.
Mitigation / rollback
The node is passive until clients send the request. To stop clients using it, drop the user-agent token; they then fall back to FIND_NODE plus GET. Otherwise revert.
🤖 Generated with Claude Code