Repository navigation
fix(layout): run the fallback script when the primary attempt hangs - #77
Merged
chelentos merged 1 commit intoOct 23, 2026
Merged
Conversation
chelentos
marked this pull request as draft
October 23, 2026 08:29
chelentos
marked this pull request as ready for review
October 23, 2026 09:14
Scripts were created with async = false, so a fallback attempt queued behind a hanging primary one in the browser's in-order execution list and was not executed although already downloaded. On timeout the stale element was only removed, which neither cancels the request nor stops the late response from executing, so the primary copy ran a second time after the fallback once the CDN finally answered. Create scripts with async = true (the loader already serializes them itself) and, on timeout, move the abandoned script into a detached document so that its late response is never executed. Fixes gravity-ui#76 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
chelentos
force-pushed
the
fix/fallback-hanging-primary
branch
from
October 23, 2026 09:18
a308974 to
f49bffa
Compare
DanilaBeseda
self-requested a review
October 23, 2026 09:29
DanilaBeseda
approved these changes
Oct 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What was wrong
With
publicPathFallbacksconfigured, the start-asset loader only recovered when the primary base failed fast. When the CDN hung and answered later thanassetRetry.timeoutMs, the fallback script was downloaded but never executed, the loader gave up after the second timeout, and the remaining entry scripts were never requested. See #76 for the full analysis.Why
createAssetElementcreated scripts withasync = false, so every attempt entered the browser's "list of scripts that will execute in order as soon as possible". The fallback attempt queued behind the hanging primary one and waited for it although it was already downloaded.element.remove(). That neither cancels the in-flight request nor removes the element from that list, so Chromium still executed the stale primary script when its response finally arrived (a second execution ofruntime.js, after the fallback copy).finish(false)setstopped = trueand the queue stopped;app.jswas never requested.What changed
async = true. Ordering does not depend onasync = false:processScriptQueuealready awaits eachloadAssetbefore taking the next script, so entry scripts still run one after another.document.implementation.createHTMLDocument('').adoptNode(el)). Per the HTML spec a script is not executed when its node document differs from the one it was prepared in, so the late response is ignored. This was verified in Chromium (see below) rather than assumed: withremove()alone the late script still runs; afteradoptNodeit does not.data-app-layout-preloadhandling and theapp-layout:asset-load-errorevent are unchanged.script timeout neutralizes the hanging primary so the fallback runs first: primary times out, fallback is inserted as an async script, the stale primary is detached from the document, the fallback's load lets the next script start, and a lateloadon the primary changes nothing. jsdom does not execute scripts, so the "never executed" part is asserted through the node-document condition and covered by the browser run below.The request to the hung CDN is not cancelled (a
<script>cannot abort its fetch); it just can no longer execute. Cancelling would requirefetch+AbortController+ an inline script, which changes caching and sourcemap behaviour, so it was not done.How it was verified
npm run lint,npm run typecheck,npm test(44 tests),npm run buildon Node 18.createLayoutPlugin,/cdn/runtime.jsanswers after 22 s,/build/answers at once, default retry options) driven by Playwright with Chromium 130, the repro's dependency pointed atnpm packof this branch.Before (published 2.8.0),
CDN_MODE=hang:After (this branch),
CDN_MODE=hang, page observed for 25 s:No
EXEC /cdn/runtime.jsat 22 s, noasset-load-error. Server log:/build/runtime.jsand/build/app.jsrequested at 10 s, nothing else.After,
CDN_MODE=404control (unchanged behaviour):Fixes #76
I hereby agree to the terms of the CLA available at: https://yandex.ru/legal/cla/?lang=en
🤖 Generated with Claude Code