Skip to content

feat(api): route stream resolution through named hosts during the chain migration - #1029

Open
rickyrombo wants to merge 1 commit into
mainfrom
mjp-play-routing
Open

feat(api): route stream resolution through named hosts during the chain migration#1029
rickyrombo wants to merge 1 commit into
mainfrom
mjp-play-routing

Conversation

@rickyrombo

@rickyrombo rickyrombo commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Runbook step 5 (cmd/genesis-writer/ROLLOUT.md). Inert unless configured.

The problem it solves

Plays are recorded by whichever node serves the audiologTrackListen runs at the top of mediorum's serveBlob, before it 307s to storage — and they never travel through the relay. So new_chain_queue, which carries ManageEntityLegacy writes across to the new chain, does not carry plays.

During the migration the fleet is split across two chains for days (step 10 moves ~10 nodes per jail cycle, ~5 hours apart). Every play recorded by an already-migrated node lands on the new chain while the indexer is still reading the old one — indexed by nobody. Roughly a quarter of plays across that window, feeding trending and rewards.

What it does

playRoutingHosts puts the named hosts first when resolving a stream URL. Point it at nodes that stay on the old chain — creatornode.audius.co and v.monophonic.digital, which are store-all and are held back as rollback anchors anyway — and every play lands on the chain the indexer is reading. Cleared at step 13, after which plays follow the serving node again.

playRoutingHosts=creatornode.audius.co,v.monophonic.digital

Design notes

Original hosts are kept as fallbacks, not replaced. Store-all nodes hold essentially everything, but replication of a fresh upload isn't instant, and a track they don't have yet must still be streamable — with the play following whichever node serves it. tryFindWorkingUrl probes in order, so a routing host that can't serve costs one request and falls through. Streaming degrades to current behaviour rather than failing.

Bandwidth is not a concern. Those nodes return a 307 to a presigned storage URL rather than streaming bytes (serve_blob.go, BlobStorageStreaming), so the added load is one request plus a URL signature per play — not audio egress. This matters because the same two nodes are also serving state-sync snapshots and are the rollback anchors.

tryFindWorkingUrl is untouched. The candidate list is rewritten before it's called, so this doesn't conflict with #1027.

Tests

  • inert when unconfigured — same pointer returned, nil-safe
  • routing hosts ordered first, since the first host that can serve records the play
  • original url and mirrors retained behind them
  • deduped against hosts already present, so nothing is probed twice
  • path and signature survive the host rewrite — mediorum parses that signature to attribute the listen, so losing it would misattribute or drop the play
  • bare hosts and full URLs normalise identically
  • unparseable link returned untouched

Confirmed failing with the routing disabled. Existing stream and download tests still pass.

Sequencing

Land before step 10 begins, since that's when the fleet starts splitting. Independent of #1018 and #1028; touches the same file as #1027 but not the same lines.

…in migration

Plays are recorded by whichever node serves the audio -- logTrackListen runs at
the top of mediorum's serveBlob, before it 307s to storage -- and they never
travel through the relay, so the queue that carries ManageEntity writes to the
new chain does not carry them.

That matters for days during the genesis migration. Nodes move to the new chain
in batches while the indexer still reads the old one, so every play recorded by
an already-migrated node lands on a chain nobody is indexing. The fleet
migration is measured in days, and roughly a quarter of plays would be lost
across that window.

playRoutingHosts, when set, puts those hosts first when resolving a stream URL.
Naming nodes that stay on the old chain keeps plays on the chain the indexer is
reading. Unset, this is inert.

The original url and mirrors are kept behind the routing hosts rather than
replaced: store-all nodes hold nearly everything, but a fresh upload has not
necessarily replicated, and such a track must still stream -- with the play
following whichever node serves it. tryFindWorkingUrl probes in order, so a
routing host that cannot serve costs one request and falls through.

Bandwidth is not a concern: those nodes 307 to presigned storage rather than
streaming bytes, so the added cost is one request and a URL signature per play.

Tests cover ordering, fallback retention, dedupe against existing mirrors, that
the path and the signature mediorum parses for attribution survive the host
rewrite, bare-host and full-URL forms, and that an unparseable link degrades to
current behaviour. Confirmed failing with the routing disabled.
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