Skip to content

feat(httpnet): dedupe traffic to shared gateways - #1151

Draft
lidel wants to merge 2 commits into
mainfrom
feat/httpnet-host-dedup
Draft

feat(httpnet): dedupe traffic to shared gateways#1151
lidel wants to merge 2 commits into
mainfrom
feat/httpnet-host-dedup

Conversation

@lidel

@lidel lidel commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

Problem

Delegated routing routinely returns several peer IDs for one HTTP gateway. Bitswap keeps one MessageQueue per peer ID, so a single broadcast want becomes N identical requests, N Connect probes, and N separate error budgets against the same upstream. A single 429 also still escalates to a disconnect, and recovery waits on provider re-discovery.

Fix

  • concurrent identical requests (same scheme, host, SNI, method, CID) share one round trip; every peer still gets its own response and per-peer accounting
  • one refcounted per-endpoint registry shares the probe result (a second peer ID connects without probing), the server-error breaker, and the 404 counters; a broken gateway disconnects all of its peer IDs after one round of failures, and the state drops when the last one leaves
  • throttling (429/502/503/504) now means "wait, don't disconnect": the endpoint cools down per Retry-After1, wants during the window resolve as DONT_HAVE with zero HTTP traffic, and requests resume when it lapses; throttles without the header back off exponentially up to DefaultMaxBackoff
  • DefaultMaxRetries is now 3, matching the MessageQueue budget: senders sharing one breaker counter must agree on the threshold

Builds on #1205. Out of scope: no bitswap-core or routing changes; peer IDs stay individually connected and addressable.

Before ramping:

  • A/B on rainbow staging: retrieval success rate unchanged or better, no probe traffic during Retry-After windows

Footnotes

  1. Path Gateway spec, Retry-After response header: gateways SHOULD return Retry-After with 429, 503 and 504. RFC 9110, section 10.2.3: Retry-After indicates how long to wait before a follow-up request.

@lidel lidel added the status/blocked Unable to be worked further until needs are met label Apr 30, 2026
@lidel lidel self-assigned this Apr 30, 2026
@codecov

codecov Bot commented Apr 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.12281% with 31 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.53%. Comparing base (25b1db8) to head (bd9deca).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
bitswap/network/httpnet/msg_sender.go 73.68% 18 Missing and 2 partials ⚠️
bitswap/network/httpnet/endpoint_tracker.go 92.07% 4 Missing and 4 partials ⚠️
bitswap/network/httpnet/httpnet.go 95.08% 2 Missing and 1 partial ⚠️

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1151      +/-   ##
==========================================
+ Coverage   64.18%   64.53%   +0.34%     
==========================================
  Files         269      270       +1     
  Lines       27193    27393     +200     
==========================================
+ Hits        17455    17678     +223     
+ Misses       8024     8001      -23     
  Partials     1714     1714              
Files with missing lines Coverage Δ
bitswap/network/httpnet/cooldown.go 100.00% <100.00%> (ø)
bitswap/network/httpnet/inflight.go 100.00% <100.00%> (ø)
bitswap/network/httpnet/httpnet.go 73.46% <95.08%> (+4.62%) ⬆️
bitswap/network/httpnet/endpoint_tracker.go 92.07% <92.07%> (ø)
bitswap/network/httpnet/msg_sender.go 74.20% <73.68%> (+9.45%) ⬆️

... and 11 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

lidel added 2 commits August 18, 2026 17:51
Delegated routing routinely returns several peer IDs for one HTTP
gateway. Bitswap creates one MessageQueue per peer ID, so a broadcast
want for one CID becomes N identical requests against the same
upstream. Concurrent requests that agree on (scheme, host, SNI,
method, CID) now share a single round trip via a small singleflight
tracker; each waiter still runs its own response handling, so
per-peer cooldowns, latency estimates and bitswap accounting match
what N separate requests would have produced.

- inflight.go: singleflight tracker; keys distinguish every field
  that changes the upstream response; no result caching (coalescing
  applies only while a request is in flight); deferred cleanup so a
  panicking leader cannot wedge waiters
- msg_sender.go: tryURL split into executeRequest (leader, one wire
  round trip, wire metrics) and handleResponse (per waiter); stats
  count logical messages per peer, wire metrics stay with the leader
N peer IDs resolving to one HTTP endpoint previously kept N copies
of every error budget and re-proved the same gateway N times.
Endpoint state now lives in one refcounted registry keyed by
(scheme, host, SNI), shared by every peer using the endpoint, and
throttling responses are treated as "wait", not "broken".

- endpoint_tracker.go replaces the per-peer error tracker: probe
  method and round trip (Connect skips the probe for endpoints
  proven by another connected peer, inheriting HEAD support and the
  latency seed), shared server-error breaker, shared client-error
  counts, headerless-throttle streak; state drops when the last
  peer disconnects
- breaker: one wire-level error charges the shared counter once no
  matter how many coalesced waiters observed it; any 200/404 resets
  it; a tripped endpoint is re-probed on Connect instead of
  inherited, and a successful probe forgives the errors
- throttle (429/502/503/504): never charges the breaker and never
  disconnects; wants during the cooldown resolve as DONT_HAVE with
  no HTTP traffic and stay neutral toward the client-error
  threshold; without Retry-After the fallback backoff doubles per
  consecutive throttle up to DefaultMaxBackoff
- senders snapshot the capped cooldown deadline instead of the raw
  Retry-After date, so one response cannot pause a sender beyond
  DefaultMaxBackoff
- DefaultMaxRetries is now 3 (was 1): the counter it gates is
  shared per endpoint, so senders must agree on one threshold; 3
  matches the budget bitswap's MessageQueue already used
- Connect and DisconnectFrom serialize per peer, so endpoint
  registration cannot interleave with release
@lidel
lidel force-pushed the feat/httpnet-host-dedup branch from eac3972 to bd9deca Compare August 18, 2026 16:14
@lidel lidel changed the title feat(bitswap/httpnet): dedupe traffic across peer IDs sharing one HTTP gateway feat(httpnet): dedupe traffic to shared gateways Aug 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status/blocked Unable to be worked further until needs are met

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant