# Multi-Consumer Frame Publisher + Back-Pressure Drops **Task**: AZ-659_frame_ingest_publisher **Name**: Tokio broadcast publisher + per-consumer drop counters + zero-copy `Arc` **Description**: Publish `Frame`s through a single multi-consumer channel using `Arc` for pixel data so consumers do not copy. Drop frames when downstream consumers fall behind beyond a configured queue depth; record per-consumer drop counters with reason tags. **Complexity**: 3 points **Dependencies**: AZ-640_initial_structure, AZ-657_frame_ingest_rtsp_session, AZ-658_frame_ingest_decoder **Component**: frame_ingest **Tracker**: AZ-659 **Epic**: AZ-627 ## Problem Three downstream consumers (`detection_client`, `movement_detector`, `telemetry_stream`) all need the same frames at the same rate. A single-consumer queue would serialise the slowest; a per-consumer fan-out with cloned pixel buffers would multiply memory. The right structure is a Tokio `broadcast` channel (or equivalent) carrying `Arc` so pixels are shared by reference. Slow consumers drop their oldest frame, with the drop counted (and reason-tagged) — never silently coalesced. ## Outcome - `FramePublisher::subscribe() -> FrameReceiver` returns a per-consumer receiver. - `Frame` carries `Arc` for `pixels` so consumers do not copy. - When a consumer falls behind beyond `channel_depth` (configurable, default 4), the oldest frame is dropped for THAT consumer; per-consumer counters increment with reason tag (`{detection_client_slow, movement_detector_slow, telemetry_slow}`). - Health surface: per-consumer drop counters, total publish count. ## Scope ### Included - `tokio::sync::broadcast` (or equivalent) with `Arc` payload. - Per-consumer drop counter (statically known three consumer ids; future-extensible). - Channel-depth config. ### Excluded - RTSP session (task 18). - Decoder (task 19). ## Acceptance Criteria **AC-1: Three consumers receive every frame at nominal rate** Given three subscribers consuming at 30 fps and source at 30 fps When the publisher runs for 10 s Then each consumer observes ~300 frames; per-consumer drop counters = 0. **AC-2: Slow consumer drops, fast consumers unaffected** Given a slow consumer that yields every 200 ms while source is 30 fps and `channel_depth = 4` When the publisher runs for 5 s Then the slow consumer's drop counter increments and fast consumers continue to receive every frame. **AC-3: Zero-copy under load** Given a publisher emitting at 30 fps for 60 s with three subscribers When peak memory is sampled Then memory does not scale linearly with consumer count (i.e. `Arc` is correctly shared). ## Non-Functional Requirements **Performance** - Publish-to-consumer p99 ≤5 ms (helps keep total RTSP-rx-to-publish under the 30 ms p99 budget). **Reliability** - Drops are counted with reason; never silent. - No unbounded memory growth on slow consumer. ## Runtime Completeness - **Named capability**: lossy multi-consumer frame fan-out with `Arc`. - **Production code that must exist**: real broadcast channel; real per-consumer drop accounting. - **Unacceptable substitutes**: cloning pixel buffers per consumer is unacceptable (multiplies memory); blocking the publisher on a slow consumer is unacceptable (gates the whole pipeline).