From 369adc999f9c4cd87732c4068c13405b5baabd99 Mon Sep 17 00:00:00 2001 From: robertjamesprior <83608739+robertjamesprior@users.noreply.github.com> Date: Fri, 16 Oct 2026 19:29:48 +0000 Subject: [PATCH] Shorten the media connect bound to fit the embedder watchdog The per-stage connect bounds are sequential, so they sum to a budget an embedder has to outlast to see why a viewer stalled. At 15s + 3s + 30s that budget was 48s, against the 15s mount-to-remount watchdog the live-view docs ship. An embedder following that snippet tears the frame down before a media-stage failure can report its reason, and media is the stage most likely to have stalled. Media drops to 5s. It only has to outlast ICE reaching connected, measured at 1-16ms direct and ~90ms relay-only, so 5s is still a wide ceiling; the stretch is only partly measured because 44 of 130 probe runs stopped at checking. The transport bound stays at 15s, since the live-view proxy can spend ~12s waking a browser before the socket opens and shortening it would report a slow wake as dead. The bounds now sum to 23s, and a test guards that ceiling. --- images/chromium-headful/client/src/neko/base.ts | 16 +++++++++++----- .../client/tests/connect-bound.test.ts | 11 +++++++++++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/images/chromium-headful/client/src/neko/base.ts b/images/chromium-headful/client/src/neko/base.ts index 37b16dea..caaa63bc 100644 --- a/images/chromium-headful/client/src/neko/base.ts +++ b/images/chromium-headful/client/src/neko/base.ts @@ -17,6 +17,10 @@ import { // only "timeout", so each stage fails on its own bound with its own reason. A // bound expiring is terminal rather than retried: a second attempt against the // same peer costs the viewer time an embedder can spend on a new session. +// +// The bounds are sequential, so they are also a budget: an embedder that gives +// up on a viewer before the last one expires never sees the reason. The three +// sum to 23s, and the watchdog in the live-view docs is set above that. export type ConnectStage = 'transport' | 'signaling' | 'media' export const CONNECT_STAGE_TIMEOUT_MS: Record = { @@ -32,11 +36,13 @@ export const CONNECT_STAGE_TIMEOUT_MS: Record = { // the remote candidates arrive in the offer — measured 1-16ms direct, ~90ms // relay-only. The bound covers through ICE `connected` rather than stopping at // `checking`, and the timings above only recorded `checking`: 44 of 130 runs - // have no `connected` timestamp, all 30 ap-southeast runs among them. Nothing - // measured the stretch the bound now waits on, so 30s is an interim ceiling for - // it rather than a calibrated one, to be replaced once per-region timings - // through a successful connect exist. - media: 30000, + // have no `connected` timestamp, all 30 ap-southeast runs among them, so the + // stretch this waits on is only partly measured. 5s is a ceiling on that + // stretch rather than a calibrated value, and is to be replaced by per-region + // timings through a successful connect. It is not 30s: an embedder that + // remounts 15s after mount — the example the live-view docs ship — throws the + // frame away before a 30s bound could report which stage stalled. + media: 5000, } // Sent to the parent frame so an embedder can pick a recovery without parsing diff --git a/images/chromium-headful/client/tests/connect-bound.test.ts b/images/chromium-headful/client/tests/connect-bound.test.ts index 6152b8e5..316fe631 100644 --- a/images/chromium-headful/client/tests/connect-bound.test.ts +++ b/images/chromium-headful/client/tests/connect-bound.test.ts @@ -420,4 +420,15 @@ describe('live view connect failures', () => { expect(posted).toHaveLength(1) }) + + // The stages run in sequence, so their bounds are a budget: an embedder that + // gives up before the last one expires never sees the reason for the stage that + // stalled. The watchdog in browsers/live-view.mdx is set above this sum, which + // is why raising a bound to 30s broke it — an embedder following that snippet + // remounted first and the media reason never arrived. Change one, change both. + test('the stage bounds fit inside the watchdog the live-view docs set', () => { + const budget = Object.values(CONNECT_STAGE_TIMEOUT_MS).reduce((sum, ms) => sum + ms, 0) + + expect(budget).toBeLessThanOrEqual(23000) + }) })