§ 8.3 Module 8 — Production: Forensics, Observability, and the Frontier
"In theory there is no difference between theory and practice. In practice there is."Course notes
By the end of this lesson
The failure that forced the design
The 'Firefox+relay' war story: a dashboard's global p95 looked healthy while one segment — Firefox users behind TURN — quietly regressed 40%. Aggregates hide; segmentation reveals. Production WebRTC observability is mostly the discipline of not fooling yourself with means.
From one call to a million: metric selection (join time, time-to-first-frame, ICE failure rate by path type), aggregation discipline, segmentation that finds regressions, and alert thresholds graded on precision and recall.
The operational claim is precise: SLO menu: connect rate, join time, TTFF, MOS proxies, TURN percentage. Treat it as an observable contract. Record the state transition or counter that proves it, test the failure path as well as the happy path, and keep units and clock domains explicit. That discipline is what separates a plausible WebRTC explanation from a production diagnosis.
Engineering consequence. SLO menu: connect rate, join time, TTFF, MOS proxies, TURN percentage. Verify the behavior with a controlled trace before interpreting a live call: establish the expected state, change one variable, and compare deltas rather than isolated values. In production, attach the observation to a timestamp and media direction so the same evidence cannot be mistaken for a different failure.
A full getStats() snapshot for a modest conference connection serialises to 30–80 KB. At 1 Hz for 10,000 concurrent participants that is on the order of gigabytes per minute of ingest, most of it identical strings and unchanging fields. Teams discover this after the first invoice, and the reflex — sample fewer sessions — biases the data toward the calls that behaved well enough to keep reporting.
Aggregate on the client: compute rates and windowed percentiles over 5–10 s, emit a fixed set of numeric fields, and send that. Keep full snapshots only for sessions flagged as bad, and send those on the failure path rather than continuously.
The operational claim is precise: Percentile discipline; cardinality traps; segment significance (n matters). Treat it as an observable contract. Record the state transition or counter that proves it, test the failure path as well as the happy path, and keep units and clock domains explicit. That discipline is what separates a plausible WebRTC explanation from a production diagnosis.
Engineering consequence. Percentile discipline; cardinality traps; segment significance (n matters). Verify the behavior with a controlled trace before interpreting a live call: establish the expected state, change one variable, and compare deltas rather than isolated values. In production, attach the observation to a timestamp and media direction so the same evidence cannot be mistaken for a different failure.
Interactive 3D instrument
The call-quality pipeline — the mean is the thing that lies
A 3D instrument you drive yourself, one variable at a time. It needs JavaScript and WebGL, so it is not shown in this static copy of the page.
The operational claim is precise: Regression hunting via worst-segment analysis. Treat it as an observable contract. Record the state transition or counter that proves it, test the failure path as well as the happy path, and keep units and clock domains explicit. That discipline is what separates a plausible WebRTC explanation from a production diagnosis.
Engineering consequence. Regression hunting via worst-segment analysis. Verify the behavior with a controlled trace before interpreting a live call: establish the expected state, change one variable, and compare deltas rather than isolated values. In production, attach the observation to a timestamp and media direction so the same evidence cannot be mistaken for a different failure.
Periodic telemetry only reports from sessions that survived long enough to report. A call that fails ICE in four seconds, a tab that crashed, and a user who force-quit all contribute nothing, so your average quality is measured over the population that did not fail. This is why quality metrics improve after a regression that increases hard failures.
Emit a beacon on the failure paths specifically: connectionState === 'failed', iceConnectionState === 'failed', and pagehide/visibilitychange with the last snapshot in hand. Use fetch(..., { keepalive: true }) or navigator.sendBeacon. Then report a join-success rate separately from quality; conflating them hides both.
The operational claim is precise: Alerting: thresholds on p95 not mean; precision/recall of alert rules. Treat it as an observable contract. Record the state transition or counter that proves it, test the failure path as well as the happy path, and keep units and clock domains explicit. That discipline is what separates a plausible WebRTC explanation from a production diagnosis.
Engineering consequence. Alerting: thresholds on p95 not mean; precision/recall of alert rules. Verify the behavior with a controlled trace before interpreting a live call: establish the expected state, change one variable, and compare deltas rather than isolated values. In production, attach the observation to a timestamp and media direction so the same evidence cannot be mistaken for a different failure.
Start with the claim “SLO menu: connect rate, join time, TTFF, MOS proxies, TURN percentage” and turn it into a falsifiable trace. Write down the input condition, the expected state transition, the counter or packet field that should change, and the deadline by which it must change. This prevents a familiar mistake: observing a symptom after several mechanisms have already reacted and assigning it to whichever mechanism has the most recognizable name. The causal order matters. A queue grows before loss; an ICE path fails before a restart selects a new pair; a decoder loses reference state before frames stop rendering. Preserve that order in the evidence.
Now hold every unrelated variable still. Use canned timestamps, SDP, stats-shaped records, or a permission-free loopback source so the same input can be replayed. Record both media directions separately. For cumulative counters, retain two samples and divide the difference by their timestamp interval. For state machines, retain the ordered event sequence rather than the last state alone. For negotiated features, compare the offer, answer, and live stats result. These three views answer different questions: what was proposed, what was accepted, and what the browser actually used.
The first lab, Find the Sick Segment, applies this discipline to a bounded implementation problem. Implement aggregateCalls(records) over 500 canned call summaries: p50/p95 join time, failure rate segmented by (relayed / network type / browser), and worstSegment(); then define alert thresholds catching 2 seeded regressions across 5 noise days. Assertions: exact percentiles, the seeded Firefox+relay regression found, alerts at 100% precision on the noise days; hints catch averaging percentiles and treating n=3 cells as significant. Before coding, identify the invariant hidden in that brief and the tempting shortcut the tests should reject. Then run the reference trace, perturb one boundary value, and explain why the result changes. If the code passes but the explanation cannot predict the perturbed result, the implementation is memorized rather than understood.
Packet loss is a poor alerting signal: NACK repairs most video loss, DTX makes audio loss percentages meaningless, and duplicates can make it negative. The metrics that correlate with complaints are (concealedSamples - silentConcealedSamples)/totalSamplesReceived for audio, qualityLimitationDurations share for video, framesDropped/framesReceived, time from join to first framesDecoded, and the share of sessions on a relay or on a TCP relay.
Define those five as your SLIs, alert on their 95th percentile rather than the mean (WebRTC quality distributions have long tails and the mean hides everything), and keep loss as a diagnostic dimension rather than an alert.
A browser loopback is deliberately cleaner than the internet. It removes signaling latency, hostile NAT behavior, relay geography, wireless contention, device heat, and competing encoders. That makes it ideal for proving protocol and API invariants, but a dangerous basis for capacity claims. Carry the invariant into a second test with a canned hostile trace: reordered packets, a delayed candidate, a missing remote stats object, a bitrate step, or a state transition that recovers before the debounce timer. The implementation should remain correct even when optional fields are absent and events arrive more than once.
Feature detection is also part of correctness. Browser support for a codec, scalability mode, encoded transform, or transport API does not imply that the negotiated peer supports it or that hardware acceleration is active. Prefer capability checks, preserve a standards-based fallback, and label any simulation honestly. The graded result should measure the learner's algorithm; an optional live coda may demonstrate the real API without making a camera, operating-system codec, or network service a hidden test dependency.
Finally, connect the mechanism to the last topic in the lesson: “Alerting: thresholds on p95 not mean; precision/recall of alert rules”. Advanced WebRTC failures cross boundaries. Congestion control changes encoder output; layer selection changes keyframe demand; end-to-end encryption constrains what an SFU can inspect; a watchdog can collide with negotiation; observability can mislabel a path if its joins are wrong. At every boundary, define ownership and an idempotent action. A retry must not create duplicate work, a stale event must not overwrite newer state, and an escalation must have a terminal cleanup path.
Use the problem set as an executable notebook. Keep a short record beside each answer: the invariant, the decisive evidence, the rejected alternative, and the production caveat. That record is more valuable than a green checkmark because it can be reused when a real call presents the same mechanism through noisier evidence.
A common error
You rarely need TURN — 10-20% of real-world sessions require relay; production without TURN (including turns: over TCP 443) is broken.
Implement the decision rules, run every assertion, then use the explanations to distinguish a wrong result from a wrong model.
Find the Sick Segment. Implement aggregateCalls(records) over 500 canned call summaries: p50/p95 join time, failure rate segmented by (relayed / network type / browser), and worstSegment(); then define alert thresholds catching 2 seeded regressions across 5 noise days. Assertions: exact percentiles, the seeded Firefox+relay regression found, alerts at 100% precision on the noise days; hints catch averaging percentiles and treating n=3 cells as significant.
Lab 1
Find the Sick Segment
Graded in the browser against 1 assertion; the editor and harness require JavaScript.
Check: Metrics That Lie. Items on why mean join time hides bimodal failure, when a segment is too small to alert on, and which single metric best proxies 'users are suffering' for a given product shape.
Check your understanding
Auto-graded check
4 auto-graded questions with an explanation for every wrong answer. Requires JavaScript. (m8-l3-ex2)
What the specifications say, and what the browsers actually do, are two different documents. These are the divergences that cost production teams their weekends.
Client wall clocks differ by seconds; performance.timeOrigin differs per tab; RTCStats.timestamp epochs differ per browser. Line up two participants' timelines on their own timestamps and a causally later event appears earlier, which makes "who caused the freeze" unanswerable — and that question is the entire point of collecting both sides.
Stamp a server timestamp into the join response, measure and store each client's offset, and normalise every beacon at ingest. Carry a single call id and a per-participant id on every record so joins are cheap. Store the raw client timestamp too, so an offset bug is recoverable.
WebRTC: From First Principles · Module 8, Lesson 3