Skip to content

fix(engine): fail-fast the sub-composition timeline wait when a script 404s#2045

Open
vanceingalls wants to merge 1 commit into
mainfrom
de2-14-subtimeline-failfast
Open

fix(engine): fail-fast the sub-composition timeline wait when a script 404s#2045
vanceingalls wants to merge 1 commit into
mainfrom
de2-14-subtimeline-failfast

Conversation

@vanceingalls

Copy link
Copy Markdown
Collaborator

Why

pollSubCompositionTimelines waits for every [data-composition-id] host to register window.__timelines[id]. When the script carrying that registration fails to load (404 / request failure), the registration can never arrive — but the poll still burned the full playerReadyTimeout (45 seconds), then warned and shipped a silently animation-less render. The engine was logging the exact load failure and waiting anyway.

Wild scale (30 days, local renders, n=89,786): the capture-setup histogram decays smoothly — 402/503/364/282/191 per 5s bucket — then spikes to 705 at the 45s bucket. A textbook timeout wall: ~1,000 renders/month across 402 distinct users, ≈15 user-hours of pure waiting, each render also delivering broken animations with only a console warn.

Found while benchmarking comp 0768f038: its "45s init" was 99% this one poll phase (139ms → 45,203ms) — its animations.js was unreachable.

What

  • Sessions record failed script resources (requestfailed + HTTP ≥ 400 responses with resourceType === "script" — extending the diagnostic listeners that already existed) in session.scriptLoadFailures.
  • The poll fail-fasts: once any script has failed, the wait cuts to a 2s grace with a loud warning naming the URL(s). Late-registering fetch-async comps are untouched — no script failure means the full 45s still applies, and a registration landing inside the grace window still wins (unit-tested).
  • Outcome telemetry: sub_timeline_wait ("ready" | "timeout" | "script_failure") on render_complete, threaded session → CapturePerfSummaryRenderPerfSummary (worst across sessions) → CLI. The wild rate becomes a direct filter instead of setup-histogram forensics.

Validation

  • Discovery comp e2e: total render ~72s → 23.1s — poll cut at 2.1s, warning names the script, render completes; healthy comp reports ready. Telemetry smoke: subTimelineWait: "script_failure" / "ready" respectively.
  • Canary suite 7/7, PSNRs identical to baseline.
  • 4 new unit tests on the poll contract (ready / fail-fast / full-timeout-when-healthy / late-registration-inside-grace).
  • Engine suite: 907 passed; the 14 failures are pre-existing on main at v0.7.42 (18 fail on a clean checkout — stash A/B verified; parseVideoElements/color-grading, untouched by this diff).
  • tsc/oxlint/oxfmt clean. The now-orphaned pollPageExpression helper was removed.

Corpus note

258/1,762 corpus comps (14%) reference local scripts missing from the corpus fetch — historical eval init timings on those comps measured this timeout, not the engine. Capture-stage ratios remain valid (both paths paid it equally).

🤖 Generated with Claude Code

…t 404s

pollSubCompositionTimelines waits for every [data-composition-id] host to
register window.__timelines[id]. When the script carrying that registration
fails to load (404 / request failure), the registration can never arrive —
but the poll still burned the full playerReadyTimeout (45s), then warned and
shipped a silently animation-less render. Wild scale: the capture-setup
histogram over 30 days of local renders decays smoothly (402/503/364/282/191
per 5s bucket) then spikes to 705 at the 45s bucket — ~1,000 renders/month
across 402 distinct users, ~15 user-hours of pure waiting.

- Sessions now record failed SCRIPT resources (requestfailed + HTTP>=400
  response, listeners that already existed for diagnostics) in
  session.scriptLoadFailures.
- pollSubCompositionTimelines takes a failure getter and cuts the wait to a
  2s grace once any script failed, with a loud warning naming the URL(s).
  Late-registering fetch-async comps are unaffected: no script failure means
  the full timeout still applies, and a registration landing inside the
  grace window still wins (tested).
- Outcome telemetry: session.subTimelineWaitOutcome ("ready" | "timeout" |
  "script_failure") -> CapturePerfSummary -> RenderPerfSummary.subTimelineWait
  (worst across sessions) -> render_complete sub_timeline_wait, so the wild
  rate becomes directly trackable instead of setup-histogram forensics.

Validation: the discovery comp (0768f038, its animations.js unreachable)
drops from ~72s to 23.1s total — poll cut at 2.1s with the script named;
healthy comp reports "ready". Canary suite 7/7 (PSNRs identical). 4 new
poll unit tests; engine suite 907 passed (14 failures are PRE-EXISTING on
main at v0.7.42 — 18 fail on a clean checkout, stash A/B verified).
tsc/oxlint/oxfmt clean.

Corpus note: 258/1,762 corpus comps (14%) reference local scripts missing
from the corpus fetch — their historical eval INIT timings measured this
timeout, not the engine. Capture-stage ratios remain valid (both paths paid
it equally).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant