Skip to content

feat(moq-net): cost-based cache-aware routing (moq-lite-06 WIP) + route-cost I-D#2179

Draft
kixelated wants to merge 5 commits into
devfrom
claude/route-cost
Draft

feat(moq-net): cost-based cache-aware routing (moq-lite-06 WIP) + route-cost I-D#2179
kixelated wants to merge 5 commits into
devfrom
claude/route-cost

Conversation

@kixelated

@kixelated kixelated commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator

What

Replaces hop count as the cluster routing metric with an accumulated route cost, carried on announcements in a new (WIP, opt-in) moq-lite-06 version, specced as an extension of the relay-hops I-D (drafts/draft-lcurley-moq-relay-hops.md).

The core model: a broadcast's announcement metadata is one shared, mutable Route (hop chain + cost). A repeat ANNOUNCE replaces it in place — same broadcast, same subscriptions, new route. Teardown belongs to the data plane (a producer dying), never to metadata: an announcement rides a session, so a route update can't change which peer the media comes from, only the advertised path/cost beyond it. Origin-level restarts happen only where the active consumer actually swaps; downstream they arrive as in-place updates, so route changes no longer cascade teardowns across the mesh.

Why

Routing today is blind to what a link actually costs and to which candidate already carries the media. In a CDN deployment two relays in one datacenter each pull the same broadcast over a metered backbone (the via-sibling route is +1 hop, so shortest-hop routing never picks it), and cross-region ties break by hash rather than toward the peer already streaming it. The parallels are classic: OSPF-style per-link costs, a Babel-style damped dynamic metric, and marginal-cost advertisement for the cache signal.

How

  • Model: broadcast::Info.route: Route — hops + base + transit in one kio cell shared by every clone. origin::Producer::refresh_broadcast re-ranks a path after a route mutation and restarts only if the winner changed.
  • Wire (lite-06 only): ANNOUNCE_BROADCAST gains two cost varints (base forwarded unchanged, transit accumulated per link). No new message: a repeat Active is the update, applied in place by the receiver. Old versions derive cost from the hop count, so ordering is bit-identical to the current (hops.len(), fnv) key.
  • Cache-aware: a relay actively carrying a broadcast (any live track producer) advertises zero transit — its upstream is already paid for.
  • Propagation: the publisher tracks each announced path's last-sent route and re-announces in place when hops/cost/activity diverge (lite-05+), including lifting or imposing exclude_hop suppression that an in-place hop update can toggle. Hop changes and cost decreases forward within a 1s tick; pure cost increases hold down 30s; receivers re-rank after a deterministic per-node jitter so mutually-preferring peers don't swap simultaneously.
  • Link pricing: cluster.connect_api entries may be {host, cost} (0 = free same-DC link), declared in SETUP so both ends price the link identically.
  • Opt-in: moq-lite-06-wip ALPN, excluded from Versions::all()/ALPNS; opt in via Versions::with_preferred. Transport accept dispatch recognizes any parseable moq ALPN (is_moq_alpn).
  • Spec: folded into draft-lcurley-moq-relay-hops: optional Link Cost on the RELAY_HOPS setup option, Route Cost + ROUTE_COST parameter sections, and a Route Updates section — re-advertisement applied in place, MUST NOT interrupt existing subscriptions.

Safety

  • Loop prevention untouched: admission (reflected-chain drop, exclude_hop) runs before ranking; a repeat announce whose chain now loops through the receiver retires the broadcast.
  • Mixed-version meshes degrade to today's behavior (worst transient: a duplicate pull, never a loop).

Tests

Wire round-trips (cost on Active, version gating); model (cost-beats-hops, base cost, refresh promotes/demotes, is_active lifecycle); real-QUIC e2e: base-cost propagation, link-cost addition, hot flip in place, and route-swap continuity — a cheaper broadcast replaces the active one at the server while the client's in-flight subscription keeps receiving frames from the original producer, its broadcast handle showing the new hops, with zero re-announce events. Full workspace suite green; clippy/fmt/doc clean; the I-D renders end-to-end.

Public API (moq-net)

broadcast::Route (new), broadcast::Info { route, origin }, Consumer::is_active, origin::Producer::refresh_broadcast, Client::with_link_cost, Setup.link_cost, Versions::with_preferred, is_moq_alpn, lite::RouteCost, Version::Lite06Wip. No separate Cost type, no ANNOUNCE_UPDATE message, no with_cost_base builder.

🤖 Generated with Claude Code

kixelated and others added 3 commits July 10, 2026 13:15
… routing

Replace hop count as the routing metric with an accumulated route cost.
The hop chain stays immutable per announcement (loop detection); cost is
the one dynamic routing input:

- ANNOUNCE_BROADCAST (lite-06) carries a cost: a publisher-set base
  forwarded unchanged plus a per-link transit accumulated hop by hop.
  Absent (older versions, plain publishes) it derives from the hop
  count, so default routing orders exactly as before.
- A relay actively carrying a broadcast advertises zero transit
  (marginal cost: its upstream path is already paid for), steering
  peers onto already-hot routes instead of opening duplicate upstream
  pulls.
- ANNOUNCE_UPDATE mutates a live announcement's cost in place, so a
  hot/cold flip doesn't ride an Ended+Active pair (which aborts
  in-flight subscriptions downstream). Increases hold down 30s and
  decreases forward within a 1s tick (dynamic metrics must be coarse
  and damped); receivers re-rank after a deterministic per-node jitter
  so mutually-preferring peers don't swap simultaneously.
- Links are priced by the dialing side: a connect_api peer entry may be
  {host, cost} (0 = free same-datacenter link), declared in SETUP so
  the acceptor prices the link identically. Default cost 1 per link.
- Lite06Wip is negotiated via the moq-lite-06-wip ALPN but excluded
  from the default version sets; endpoints opt in explicitly
  (Versions::with_preferred) until lite-06 ships.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Post-merge fixups after pulling origin/dev:
- dev replaced BroadcastState.tracks (HashMap) with the amortized-GC
  WeakCache; adapt Consumer::is_active to a new WeakCache::has_live()
  query (the old code iterated .values()).
- Add drafts/draft-lcurley-moq-route-cost.md: the IETF I-D for the
  cost-based routing this branch implements, written as a moqt extension
  that composes with (and generalizes) draft-lcurley-moq-relay-hops --
  base + transit cost, per-link costs in SETUP, the marginal-cost reset
  for actively-forwarded broadcasts, and in-place cost updates.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @kixelated, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

kixelated and others added 2 commits July 11, 2026 17:36
Route cost is a strict generalization of relay-hops path selection (it
replaces the hop count with an additive weight and defaults back to hop
count when absent), and rides the same HOP_PATH machinery, so it belongs
in one draft rather than a separate overlapping one.

- Delete draft-lcurley-moq-route-cost.md.
- Extend draft-lcurley-moq-relay-hops.md: RELAY_HOPS Setup Option now
  optionally carries a Link Cost; new Route Cost + ROUTE_COST Parameter
  sections (base + transit, marginal-cost reset, relay forwarding rule,
  cost updates + damping); Path Selection made cost-primary with hop
  count as the fallback; Security + IANA (ROUTE_COST 0x40B59) updated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
API feedback: the Cost cell bolted onto an otherwise-immutable Info was
arbitrary — the hop list is announcement metadata too, and the criterion
for tearing anything down was never "which field changed" but "did the
data source change". An announcement rides a session, so a route update
never changes which peer the media comes from; teardown belongs to the
data plane (producer dies -> resubscribe), not to metadata.

- broadcast::Info now holds one shared, mutable Route (hops + base +
  transit) replacing the immutable hops field and the Cost cell. Route
  exposes hops()/contains()/base()/transit()/cost()/set()/set_cost().
- ANNOUNCE_UPDATE is gone from the wire: a repeat Active already carries
  hops + cost, and the receiver now applies it IN PLACE on the same
  broadcast — no producer replacement, no aborted subscriptions. The
  lite-06 wire delta shrinks to the two cost varints on Active.
- subscriber: restart_announce (publish-new + retire-old, which tore
  down in-flight tracks one hop below every route change) is replaced by
  update_announce (Route::set + damped refresh). A repeat announce whose
  chain now loops through us retires the broadcast as before.
- publisher: the cost tick generalizes to a route tick (lite-05+): it
  re-announces in place when hops, cost, or activity diverge from what
  the peer last saw, including lifting/imposing exclude_hop suppression
  that an in-place hop update can toggle. Hop changes and cost decreases
  forward promptly; pure cost increases keep the 30s hold-down.
- origin restarts now happen ONLY where the active consumer actually
  swaps (that relay re-announces; downstream applies it in place), so a
  route change no longer cascades teardowns across the mesh.
- e2e: the route-cost test now also proves swap continuity — a cheaper
  broadcast replaces the active one at the server and the client's
  in-flight subscription keeps receiving frames from the original
  producer while its broadcast handle shows the new hops, with no
  re-announce events.
- I-D: Cost Updates becomes Route Updates — re-advertisement is the
  update mechanism, applied in place; updates MUST NOT interrupt
  existing subscriptions; the lightweight cost-only message is dropped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kixelated
kixelated marked this pull request as draft July 18, 2026 12:20
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