diff --git a/architecture.md b/architecture.md index 5048dce..bb1b584 100644 --- a/architecture.md +++ b/architecture.md @@ -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. +### 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 graph TD 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? - 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? +- 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.