router
¶
Multi-replica load balancer + WebSocket proxy for the streaming server.
Sits in front of one-or-more streaming-server replicas and forwards
WebSocket sessions to a healthy primary, with failover to secondaries.
Kept in-repo under fastvideo/entrypoints/streaming/router/ per the
PR plan's default; the alternative (separate package) is an open
question deferred to review.
Classes¶
fastvideo.entrypoints.streaming.router.ReplicaRegistry
¶
ReplicaRegistry(replicas: list[ReplicaEndpoint])
Stateful map of replica URL → :class:Replica.
Selection favors primary replicas when healthy; otherwise the first
healthy non-primary is returned. When none are healthy, the
registry returns None so the router can reject incoming
sessions with gpu_unavailable.
Source code in fastvideo/entrypoints/streaming/router/registry.py
Functions¶
fastvideo.entrypoints.streaming.router.ReplicaRegistry.select
¶
Pick the best healthy replica.
Priority order:
- The first healthy primary (insertion order).
- The first healthy non-primary (insertion order).
Nonewhen nothing is healthy.
This MVP picks the first match within each tier; it does NOT load-balance across multiple healthy replicas of the same tier. Round-robin and weighted distribution are deferred until a real N-way active deployment exists.
Source code in fastvideo/entrypoints/streaming/router/registry.py
fastvideo.entrypoints.streaming.router.RouterConfig
dataclass
¶
RouterConfig(host: str = '0.0.0.0', port: int = 9000, replicas: list[ReplicaEndpoint] = list(), health_check_path: str = '/health', health_check_interval_seconds: float = 5.0, health_check_timeout_seconds: float = 2.0, failure_threshold: int = 3, recovery_threshold: int = 2)
Typed router config loaded from a YAML file.
Example::
router:
host: 0.0.0.0
port: 9000
replicas:
- url: http://streamer-a:8000
primary: true
- url: http://streamer-b:8000
health_check:
path: /health
interval_seconds: 5
failure_threshold: 3
Validation runs in __post_init__: empty replicas, non-positive
intervals/timeouts, thresholds < 1, non-http(s) URLs, and more than
one primary all raise ValueError so misconfigurations surface at
load time rather than as confusing runtime failures.
Functions¶
fastvideo.entrypoints.streaming.router.build_router_app
¶
build_router_app(config: RouterConfig, *, registry: ReplicaRegistry | None = None) -> FastAPI
Build the router FastAPI app.
registry can be injected for tests; defaults to one built from
config.replicas.
Source code in fastvideo/entrypoints/streaming/router/main.py
Modules¶
fastvideo.entrypoints.streaming.router.config
¶
Typed router configuration.
Classes¶
fastvideo.entrypoints.streaming.router.config.ReplicaEndpoint
dataclass
¶
fastvideo.entrypoints.streaming.router.config.RouterConfig
dataclass
¶
RouterConfig(host: str = '0.0.0.0', port: int = 9000, replicas: list[ReplicaEndpoint] = list(), health_check_path: str = '/health', health_check_interval_seconds: float = 5.0, health_check_timeout_seconds: float = 2.0, failure_threshold: int = 3, recovery_threshold: int = 2)
Typed router config loaded from a YAML file.
Example::
router:
host: 0.0.0.0
port: 9000
replicas:
- url: http://streamer-a:8000
primary: true
- url: http://streamer-b:8000
health_check:
path: /health
interval_seconds: 5
failure_threshold: 3
Validation runs in __post_init__: empty replicas, non-positive
intervals/timeouts, thresholds < 1, non-http(s) URLs, and more than
one primary all raise ValueError so misconfigurations surface at
load time rather than as confusing runtime failures.
fastvideo.entrypoints.streaming.router.main
¶
Router FastAPI entry point.
Exposes the same /v1/stream WebSocket path the backend servers do,
accepts a client, picks a healthy replica from the registry, and
proxies frames bidirectionally.
PR 7.9 ships the minimum-viable shape: explicit replica list, single
primary, JSON + binary passthrough in both directions, and a
/status endpoint for operators. Sticky-session routing (so a
reconnect lands on the same backend) is left for a follow-up.
Classes¶
Functions¶
fastvideo.entrypoints.streaming.router.main.build_router_app
¶
build_router_app(config: RouterConfig, *, registry: ReplicaRegistry | None = None) -> FastAPI
Build the router FastAPI app.
registry can be injected for tests; defaults to one built from
config.replicas.
Source code in fastvideo/entrypoints/streaming/router/main.py
fastvideo.entrypoints.streaming.router.registry
¶
Replica registry + health-check loop.
The registry tracks the set of known backend replicas and their live health. The router consults it for "pick a backend for this session" decisions and a background task updates it from periodic HTTP probes.
State machine per replica::
HEALTHY ──(N consecutive failures)──▶ UNHEALTHY
▲ │
└──────(M consecutive successes)──────┘
Where N = :attr:RouterConfig.failure_threshold and
M = :attr:RouterConfig.recovery_threshold.
Attributes¶
fastvideo.entrypoints.streaming.router.registry.HttpProbe
module-attribute
¶
HttpProbe = Any
Structural alias for health-probe callables. Concrete signature is
async def __call__(url: str, *, timeout: float) -> tuple[float,
str | None]; typing.Callable cannot express keyword-only parameters,
so duck-typing is the pragmatic compromise.
Classes¶
fastvideo.entrypoints.streaming.router.registry.ReplicaRegistry
¶
ReplicaRegistry(replicas: list[ReplicaEndpoint])
Stateful map of replica URL → :class:Replica.
Selection favors primary replicas when healthy; otherwise the first
healthy non-primary is returned. When none are healthy, the
registry returns None so the router can reject incoming
sessions with gpu_unavailable.
Source code in fastvideo/entrypoints/streaming/router/registry.py
Functions¶
fastvideo.entrypoints.streaming.router.registry.ReplicaRegistry.select
¶Pick the best healthy replica.
Priority order:
- The first healthy primary (insertion order).
- The first healthy non-primary (insertion order).
Nonewhen nothing is healthy.
This MVP picks the first match within each tier; it does NOT load-balance across multiple healthy replicas of the same tier. Round-robin and weighted distribution are deferred until a real N-way active deployment exists.
Source code in fastvideo/entrypoints/streaming/router/registry.py
Functions¶
fastvideo.entrypoints.streaming.router.registry.run_health_check_loop
async
¶
run_health_check_loop(registry: ReplicaRegistry, config: RouterConfig, *, stop_event: Event, http_get: HttpProbe | None = None) -> None
Poll all replicas' health endpoints in parallel on a fixed interval.
http_get is pluggable so unit tests can inject a deterministic
probe without hitting the network. The default builds a single
httpx.AsyncClient shared across the loop's lifetime so the
common case (steady polling against a stable replica set) reuses
TCP/TLS connections instead of paying handshake cost per probe.
Probes within one polling cycle run concurrently via asyncio.gather
so a slow replica doesn't push the cycle past
health_check_interval_seconds.