docs: add relay scheduler section to architecture

Documents multi-input scheduling as a distinct concern from delivery
mode. Covers strict priority, round-robin, weighted round-robin, deficit
round-robin, and source suppression policies. Notes that the relay module
should expose a pluggable scheduler interface. Adds scheduler policy
selection to Open Questions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-27 06:21:49 +00:00
parent 08ee9a2eb0
commit 24d031d42b

View File

@@ -237,6 +237,26 @@ The interaction between outbound congestion and the byte budget is important: a
Even though the relay does not apply backpressure, it should emit **observable congestion signals** — drop counts, queue depth, byte utilization — on the control plane so that the controller can make decisions: reduce upstream quality, reroute, alert, or adjust budgets dynamically. Even though the relay does not apply backpressure, it should emit **observable congestion signals** — drop counts, queue depth, byte utilization — on the control plane so that the controller can make decisions: reduce upstream quality, reroute, alert, or adjust budgets dynamically.
### Multi-Input Scheduling
When a relay has multiple input sources feeding the same output, it needs a policy for which source's frame to forward next when the link is under pressure or when frames from multiple sources are ready simultaneously. This policy is the **scheduler**.
The scheduler is a separate concern from delivery mode (low-latency vs completeness) — delivery mode governs buffering and drop behaviour per output; the scheduler governs which input is served when multiple compete.
Candidate policies (not exhaustive — the design should keep the scheduler pluggable):
| Policy | Behaviour |
|---|---|
| **Strict priority** | Always prefer the highest-priority source; lower-priority sources are only forwarded when no higher-priority frame is pending |
| **Round-robin** | Cycle evenly across all active inputs — one frame from each in turn |
| **Weighted round-robin** | Each input has a weight; forwarding interleaves at the given ratio (e.g. 1:3 means one frame from source A per three from source B) |
| **Deficit round-robin** | Byte-fair rather than frame-fair variant of weighted round-robin; useful when sources have very different frame sizes |
| **Source suppression** | A congested or degraded link simply stops forwarding from a given input entirely until conditions improve |
Priority remains a property of the path (set at connection time). The scheduler uses those priorities plus runtime state (queue depths, drop rates) to make per-frame decisions.
The `relay` module should expose a scheduler interface so policies are interchangeable without touching routing logic. Which policies to implement first is an open question — see [Open Questions](#open-questions).
```mermaid ```mermaid
graph TD graph TD
UP1[Upstream Source A] -->|encapsulated stream| RELAY[Relay] UP1[Upstream Source A] -->|encapsulated stream| RELAY[Relay]
@@ -598,3 +618,4 @@ A fully-qualified node address is `site_id:namespace:instance`. Within a single
- When a relay has multiple inputs on an encapsulated transport, how are streams tagged on the outbound side — same stream_id passthrough, or remapped? - When a relay has multiple inputs on an encapsulated transport, how are streams tagged on the outbound side — same stream_id passthrough, or remapped?
- What transport is used for relay edges — TCP, UDP, shared memory for local hops? - What transport is used for relay edges — TCP, UDP, shared memory for local hops?
- Should per-output byte budgets be hard limits or soft limits with hysteresis? - Should per-output byte budgets be hard limits or soft limits with hysteresis?
- Which relay scheduler policies should be implemented first, and what is the right interface for plugging in a custom policy? Minimum viable set is probably strict priority + weighted round-robin.