Skip to content

design: usage stats in the model (BroadcastInfo-carried, origin-attributed)#1894

Open
kixelated wants to merge 1 commit into
devfrom
claude/stats-redesign
Open

design: usage stats in the model (BroadcastInfo-carried, origin-attributed)#1894
kixelated wants to merge 1 commit into
devfrom
claude/stats-redesign

Conversation

@kixelated

Copy link
Copy Markdown
Collaborator

A design doc only — the proposed architecture for per-broadcast usage stats, to settle the shape before implementing. It supersedes the with_meter / set_meter approach currently on #1873.

The idea

Put the usage sinks in BroadcastInfo, set them at construction, and let the immutable Arc<BroadcastInfo> carry them to every track/group/frame. Usage is atomics, so the model bumps through a shared &Arc<Usage> — no mutation, so no with_meter setter and no Arc::make_mut.

  • Ingress is baked in when the publisher builds the broadcast (it has its session's sink).
  • Egress is attributed by the per-session OriginConsumer: when it hands out a BroadcastConsumer it builds that consumer's own Arc<BroadcastInfo> with this session's egress sink — so per-tier egress survives, with zero mutation, and with_meter disappears from every handler/gateway.
  • Live viewer/publisher counts move into the model: a BroadcastConsumer is one live viewer while it has ≥1 outstanding TrackConsumer. This replaces the stats-layer SessionBroadcasts sentinel and fixes the fragile split where the payload sink came from a non-bumping subscriber_meter() while the lifecycle guard lived elsewhere.
  • Arc<TrackInfo> is threaded to the group too, so timescale stops being passed separately.

Full writeup, type sketches, flows, and a 3-phase plan are in rs/moq-net/DESIGN-stats.md.

Deliberately out of scope

  • The binary Tier (internal/external) split, and the published stats schema — both unchanged here. The intent behind tiers is real (billable customer traffic vs cluster forwarding), but encoding it as a fixed enum is rigid; moving attribution to per-auth-root keying is a cleaner model that reshapes the output format, so it's a separate change.

Looking for a sign-off on the shape (especially construction-time attribution and the origin-injection hook) before I sink the ~1500-line implementation.

🤖 Generated with Claude Code

(Written by Claude)

Proposed architecture for per-broadcast usage stats, superseding the
with_meter/set_meter approach on the stats-in-model branch (#1873).

Sinks live in BroadcastInfo, set at construction; the immutable Arc<BroadcastInfo>
carries them to every track/group/frame, which bump the atomics through a shared
reference (no setter, no Arc::make_mut). The origin attributes egress per consuming
session, and the model tracks live viewer/publisher counts. Tier and the published
schema are intentionally left alone (the auth-root rework is a separate change).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
frames: AtomicU64,
bytes: AtomicU64,
// lifecycle, bumped as broadcast handles open/close (live = opened - closed)
opened: AtomicU64,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

rename to active?

We only care about BroadcastConsumers that have at least one TrackConsumer instance open. Otherwise we're just counting the number of sessions where a broadcast is announced, which I guess is cool, but IDK.

How about announced: Gauge, and active: Gauge. Gauge is two atomics, open and closed.

pub struct BroadcastInfo {
pub hops: OriginList,
pub epoch: SystemTime,
pub stats: BroadcastStats,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Maybe make this Arc instead of producer/consumer? So there's one item on the heap instead of two.

The model owns "is this broadcast being watched / published," replacing
`SessionBroadcasts`:

- A `BroadcastConsumer` counts as **one live viewer while it has ≥ 1 outstanding

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

yeah some Arc that holds an Arc. when all TrackConsumers are dropped, the BroadcastActive is dropped and we update the Gauge. Maybe a bool on if it's a producer or consumer, or just make a separate type to avoid the branch IDK. Or have Arc like the plan suggests and ignore my previous comment.


## Out of scope (deliberately)

- **The `Tier` (internal/external) split.** It's kept as-is here. The intent

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I think Origin should flag if a producer/consumer is internal/external? IDK there's something wrong with the Origin abstraction that needs to be untangled but I don't know how.

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