-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
perf: prioritize the main bundle over <link rel=preload>
s
#23556
Conversation
/ok-to-test |
Tests running at: https://github.com/appsmithorg/appsmith/actions/runs/5042569965. |
This PR has not seen activitiy for a while. It will be closed in 7 days unless further activity is detected. |
/ok-to-test |
Tests running at: https://github.com/appsmithorg/appsmith/actions/runs/5118173965. |
app/client/craco.build.config.js
Outdated
"htmlWebpackPlugin.userOptions must be defined", | ||
); | ||
|
||
htmlWebpackPlugin.userOptions.inject = "head"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iamakulov , What happens if we put this main at the end of body? Does it have to compete for bandwidth with preloads?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this request was scheduled late-ish in my tests (due to being discovered later), and one or two <link rel="preload">
stole its bandwidth. I’ll re-run the tests and get back with waterfalls in a bit!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, re-ran the tests. With <script>
in the end of <head>
(as in this PR), the main
bundle takes 2.1s (request 5):
HTML code
<!doctype html>
<html lang="en">
<head>
<script>navigator.serviceWorker.register = () => { };</script>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no" />
<title>Appsmith</title>
<style>
#loader {
position: fixed;
left: 0;
top: 0;
height: 4px;
background: #d7d7d7;
transition: all ease-in .3s
}
</style>
<link rel="preload" as="script" href="/static/js/2209.1a60b0b6.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/55178.ef82f381.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/81621.58d65afe.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/89532.5ad27563.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/85342.fd2d6599.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/3638.68e4f960.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/51863.ac47ac64.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/global-search.3bd25387.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/82492.1f73e40c.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/66124.a789a752.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/7207.9189ae78.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/29020.74dfd06a.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/72527.ce5b870f.chunk.js" fetchpriority="low" />
<link rel="preload" as="style" href="/static/css/editor.6f95ab40.chunk.css" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/editor.8486711d.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/2209.1a60b0b6.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/55178.ef82f381.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/51863.ac47ac64.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/global-search.3bd25387.chunk.js" fetchpriority="low" />
<script>const parseConfig = e => { if (0 === e.indexOf("__") || 0 === e.indexOf("$") || 0 === e.indexOf("%")) return ""; const o = e.trim(); return "false" !== o.toLowerCase() && "" !== o && ("true" === o.toLowerCase() || o) }, CLOUD_HOSTING = parseConfig("true"), ZIPY_KEY = parseConfig(""), AIRGAPPED = parseConfig("false"); if (CLOUD_HOSTING && ZIPY_KEY && !AIRGAPPED) { const e = document.createElement("script"); e.crossOrigin = "anonymous", e.defer = !0, e.src = "https://cdn.zipy.ai/sdk/v1.0/zipy.min.umd.js", e.onload = () => { window.zipy && window.zipy.init(ZIPY_KEY) }; const o = document.getElementsByTagName("head")[0]; o && o.appendChild(e) } gapiLoaded = () => { window.googleAPIsLoaded = !0 }, onError = () => { window.googleAPIsLoaded = !1 }</script>
<script async defer="defer" id="googleapis" src="https://apis.google.com/js/api.js" onload="gapiLoaded()"
onerror="onError()"></script>
<script src="/static/js/main.c65af675.js"></script>
<link href="/static/css/main.c3722dd4.css" rel="stylesheet">
</head>
<body class="appsmith-light-theme"><noscript>You need to enable JavaScript to run this app.</noscript>
<div id="loader" style="width:30vw"></div>
<div id="header-root"></div>
<div id="root"></div>
<script
type="text/javascript">const getIsLocalStorageSupported = () => { try { return window.localStorage.setItem("test", "testA"), window.localStorage.removeItem("test"), !0 } catch (e) { return !1 } }, isLocalStorageSupported = getIsLocalStorageSupported(), handleLocalStorageNotSupportedError = () => { console.error("Localstorage storage is not supported on your device.") }, localStorageUtil = { getItem: e => { if (isLocalStorageSupported) return window.localStorage.getItem(e); handleLocalStorageNotSupportedError() }, removeItem: e => { if (isLocalStorageSupported) return window.localStorage.removeItem(e); handleLocalStorageNotSupportedError() }, setItem: (e, r) => { if (isLocalStorageSupported) return window.localStorage.setItem(e, r); handleLocalStorageNotSupportedError() } }; window.addEventListener("DOMContentLoaded", (e => { document.getElementById("loader").style.width = "50vw" })); const registerPageServiceWorker = () => { "serviceWorker" in navigator && !window.Cypress && window.addEventListener("load", (function () { navigator.serviceWorker.register("/pageService.js").catch((e => { console.error("Service Worker Registration failed: " + e) })) })) }; "serviceWorker" in navigator && !window.Cypress && window.addEventListener("load", (function () { navigator.serviceWorker.register("/pageService.js").catch((e => { console.error("Service Worker Registration failed: " + e) })) }))</script>
<script
type="text/javascript">const LOG_LEVELS = ["debug", "error"], CONFIG_LOG_LEVEL_INDEX = LOG_LEVELS.indexOf(parseConfig("")), INTERCOM_APP_ID = parseConfig("%REACT_APP_INTERCOM_APP_ID%") || parseConfig("y10e7138"), DISABLE_INTERCOM = parseConfig("") || parseConfig("false"); INTERCOM_APP_ID.length && !DISABLE_INTERCOM && function () { var _ = window, e = _.Intercom; if ("function" == typeof e) e("reattach_activator"), e("update", _.intercomSettings); else { var E = document, a = function () { a.c(arguments) }; a.q = [], a.c = function (_) { a.q.push(_) }, _.Intercom = a; var I = function () { var _ = E.createElement("script"); _.type = "text/javascript", _.async = !0, _.src = "https://widget.intercom.io/widget/" + INTERCOM_APP_ID; var e = E.getElementsByTagName("script")[0]; e.parentNode.insertBefore(_, e) }; "complete" === document.readyState ? I() : _.attachEvent ? _.attachEvent("onload", I) : _.addEventListener("load", I, !1) } }(), window.SENTRY_CONFIG = parseConfig("https://abf15a075d1347969df44c746cca7eaa@o296332.ingest.sentry.io/1546547"), window.APPSMITH_FEATURE_CONFIGS = { sentry: { dsn: parseConfig("https://abf15a075d1347969df44c746cca7eaa@o296332.ingest.sentry.io/1546547"), release: parseConfig(""), environment: parseConfig("Production") }, smartLook: { id: parseConfig("c370af0df0edf38360adbefbdc47d2b42ea137c9") }, enableRapidAPI: parseConfig(""), segment: { apiKey: parseConfig("9OnZ6LnDztuZZo4zXfoutEEBB2wftHUH"), ceKey: parseConfig("AGuvNK0MfQgtxwBMCia7fRNxTjfoBRec") }, fusioncharts: { licenseKey: parseConfig("5bD2nuyE1C3C11D3E3E3C1G3F2I4B3C8xgaD3D2E3qG2A1A4dgrE2F4D1G-7xoD1D3D1lttA32B2B9C3B5D2C4B1D3H4A9kcC-13E3D4HH2sudB2D3D2A-9hH1CB5B8dfwB4G1H4I2A3C2C1D6B1E1C4G1C3B5o==") }, enableMixpanel: parseConfig("9OnZ6LnDztuZZo4zXfoutEEBB2wftHUH"), algolia: { apiId: parseConfig("AZ2Z9CJSJ0"), apiKey: parseConfig("dfde934d9bdc2e0b14830f1dd3cb240f"), indexName: parseConfig("omnibar_docusaurus_index") }, logLevel: CONFIG_LOG_LEVEL_INDEX > -1 ? LOG_LEVELS[CONFIG_LOG_LEVEL_INDEX] : LOG_LEVELS[1], cloudHosting: CLOUD_HOSTING, enableTNCPP: parseConfig("true"), appVersion: { id: parseConfig(""), releaseDate: parseConfig("") }, intercomAppID: INTERCOM_APP_ID, mailEnabled: parseConfig("true"), cloudServicesBaseUrl: parseConfig("https://cs.appsmith.com") || "https://cs.appsmith.com", googleRecaptchaSiteKey: parseConfig("6LfXDPIaAAAAAKUUoWxXi35KN7VIlXoD2rtI5gg8"), hideWatermark: parseConfig(""), disableIframeWidgetSandbox: parseConfig("true"), customerPortalUrl: parseConfig("") || "https://customer.appsmith.com", pricingUrl: parseConfig("") || "https://www.appsmith.com/pricing", airGapped: parseConfig("false") }</script>
</body>
</html>
With script in the end of <body>
, the main bundle takes 3s (request 6):
HTML code
<!doctype html>
<html lang="en">
<head>
<script>navigator.serviceWorker.register = () => { };</script>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no" />
<title>Appsmith</title>
<style>
#loader {
position: fixed;
left: 0;
top: 0;
height: 4px;
background: #d7d7d7;
transition: all ease-in .3s
}
</style>
<link rel="preload" as="script" href="/static/js/2209.1a60b0b6.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/55178.ef82f381.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/81621.58d65afe.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/89532.5ad27563.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/85342.fd2d6599.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/3638.68e4f960.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/51863.ac47ac64.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/global-search.3bd25387.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/82492.1f73e40c.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/66124.a789a752.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/7207.9189ae78.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/29020.74dfd06a.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/72527.ce5b870f.chunk.js" fetchpriority="low" />
<link rel="preload" as="style" href="/static/css/editor.6f95ab40.chunk.css" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/editor.8486711d.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/2209.1a60b0b6.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/55178.ef82f381.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/51863.ac47ac64.chunk.js" fetchpriority="low" />
<link rel="preload" as="script" href="/static/js/global-search.3bd25387.chunk.js" fetchpriority="low" />
<script>const parseConfig = e => { if (0 === e.indexOf("__") || 0 === e.indexOf("$") || 0 === e.indexOf("%")) return ""; const o = e.trim(); return "false" !== o.toLowerCase() && "" !== o && ("true" === o.toLowerCase() || o) }, CLOUD_HOSTING = parseConfig("true"), ZIPY_KEY = parseConfig(""), AIRGAPPED = parseConfig("false"); if (CLOUD_HOSTING && ZIPY_KEY && !AIRGAPPED) { const e = document.createElement("script"); e.crossOrigin = "anonymous", e.defer = !0, e.src = "https://cdn.zipy.ai/sdk/v1.0/zipy.min.umd.js", e.onload = () => { window.zipy && window.zipy.init(ZIPY_KEY) }; const o = document.getElementsByTagName("head")[0]; o && o.appendChild(e) } gapiLoaded = () => { window.googleAPIsLoaded = !0 }, onError = () => { window.googleAPIsLoaded = !1 }</script>
<script async defer="defer" id="googleapis" src="https://apis.google.com/js/api.js" onload="gapiLoaded()"
onerror="onError()"></script>
<link href="/static/css/main.c3722dd4.css" rel="stylesheet">
</head>
<body class="appsmith-light-theme"><noscript>You need to enable JavaScript to run this app.</noscript>
<div id="loader" style="width:30vw"></div>
<div id="header-root"></div>
<div id="root"></div>
<script
type="text/javascript">const getIsLocalStorageSupported = () => { try { return window.localStorage.setItem("test", "testA"), window.localStorage.removeItem("test"), !0 } catch (e) { return !1 } }, isLocalStorageSupported = getIsLocalStorageSupported(), handleLocalStorageNotSupportedError = () => { console.error("Localstorage storage is not supported on your device.") }, localStorageUtil = { getItem: e => { if (isLocalStorageSupported) return window.localStorage.getItem(e); handleLocalStorageNotSupportedError() }, removeItem: e => { if (isLocalStorageSupported) return window.localStorage.removeItem(e); handleLocalStorageNotSupportedError() }, setItem: (e, r) => { if (isLocalStorageSupported) return window.localStorage.setItem(e, r); handleLocalStorageNotSupportedError() } }; window.addEventListener("DOMContentLoaded", (e => { document.getElementById("loader").style.width = "50vw" })); const registerPageServiceWorker = () => { "serviceWorker" in navigator && !window.Cypress && window.addEventListener("load", (function () { navigator.serviceWorker.register("/pageService.js").catch((e => { console.error("Service Worker Registration failed: " + e) })) })) }; "serviceWorker" in navigator && !window.Cypress && window.addEventListener("load", (function () { navigator.serviceWorker.register("/pageService.js").catch((e => { console.error("Service Worker Registration failed: " + e) })) }))</script>
<script
type="text/javascript">const LOG_LEVELS = ["debug", "error"], CONFIG_LOG_LEVEL_INDEX = LOG_LEVELS.indexOf(parseConfig("")), INTERCOM_APP_ID = parseConfig("%REACT_APP_INTERCOM_APP_ID%") || parseConfig("y10e7138"), DISABLE_INTERCOM = parseConfig("") || parseConfig("false"); INTERCOM_APP_ID.length && !DISABLE_INTERCOM && function () { var _ = window, e = _.Intercom; if ("function" == typeof e) e("reattach_activator"), e("update", _.intercomSettings); else { var E = document, a = function () { a.c(arguments) }; a.q = [], a.c = function (_) { a.q.push(_) }, _.Intercom = a; var I = function () { var _ = E.createElement("script"); _.type = "text/javascript", _.async = !0, _.src = "https://widget.intercom.io/widget/" + INTERCOM_APP_ID; var e = E.getElementsByTagName("script")[0]; e.parentNode.insertBefore(_, e) }; "complete" === document.readyState ? I() : _.attachEvent ? _.attachEvent("onload", I) : _.addEventListener("load", I, !1) } }(), window.SENTRY_CONFIG = parseConfig("https://abf15a075d1347969df44c746cca7eaa@o296332.ingest.sentry.io/1546547"), window.APPSMITH_FEATURE_CONFIGS = { sentry: { dsn: parseConfig("https://abf15a075d1347969df44c746cca7eaa@o296332.ingest.sentry.io/1546547"), release: parseConfig(""), environment: parseConfig("Production") }, smartLook: { id: parseConfig("c370af0df0edf38360adbefbdc47d2b42ea137c9") }, enableRapidAPI: parseConfig(""), segment: { apiKey: parseConfig("9OnZ6LnDztuZZo4zXfoutEEBB2wftHUH"), ceKey: parseConfig("AGuvNK0MfQgtxwBMCia7fRNxTjfoBRec") }, fusioncharts: { licenseKey: parseConfig("5bD2nuyE1C3C11D3E3E3C1G3F2I4B3C8xgaD3D2E3qG2A1A4dgrE2F4D1G-7xoD1D3D1lttA32B2B9C3B5D2C4B1D3H4A9kcC-13E3D4HH2sudB2D3D2A-9hH1CB5B8dfwB4G1H4I2A3C2C1D6B1E1C4G1C3B5o==") }, enableMixpanel: parseConfig("9OnZ6LnDztuZZo4zXfoutEEBB2wftHUH"), algolia: { apiId: parseConfig("AZ2Z9CJSJ0"), apiKey: parseConfig("dfde934d9bdc2e0b14830f1dd3cb240f"), indexName: parseConfig("omnibar_docusaurus_index") }, logLevel: CONFIG_LOG_LEVEL_INDEX > -1 ? LOG_LEVELS[CONFIG_LOG_LEVEL_INDEX] : LOG_LEVELS[1], cloudHosting: CLOUD_HOSTING, enableTNCPP: parseConfig("true"), appVersion: { id: parseConfig(""), releaseDate: parseConfig("") }, intercomAppID: INTERCOM_APP_ID, mailEnabled: parseConfig("true"), cloudServicesBaseUrl: parseConfig("https://cs.appsmith.com") || "https://cs.appsmith.com", googleRecaptchaSiteKey: parseConfig("6LfXDPIaAAAAAKUUoWxXi35KN7VIlXoD2rtI5gg8"), hideWatermark: parseConfig(""), disableIframeWidgetSandbox: parseConfig("true"), customerPortalUrl: parseConfig("") || "https://customer.appsmith.com", pricingUrl: parseConfig("") || "https://www.appsmith.com/pricing", airGapped: parseConfig("false") }</script>
<script src="/static/js/main.c65af675.js"></script>
</body>
</html>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/ok-to-test |
Tests running at: https://github.com/appsmithorg/appsmith/actions/runs/5152566123. |
This ensures developers won’t write code that works in dev but not in prod
…eeze` to `initAndRenderApp`
Alright, so to merge this, we need to:
|
@iamakulov , We are no longer using design-system-old. |
/ok-to-test |
/build-deploy-preview |
Tested this PR. |
As discussed with @tanvibhakta this PR cannot be tested because:
|
Should I try adding tooltips to some components just for the test purposes? Edit: Storybook should also, presumably, confirm whether tooltips still work. That wouldn’t be a proper end-to-end test, though. |
Wait hmmm no, this is what’s the actual issue with this PR (and also a thing that’s failing Cypress tests). This is weird, looking into this now. |
This was happening because the bundle now loads in `<head>`, and `document.getElementById("header-root")` is now undefined when executed at the top level
Alright, fixed the header not being rendered (very silly omission; c22ac38) and checked that there are no more top-level The tooltip seems to work: |
/ok-to-test |
Tests running at: https://github.com/appsmithorg/appsmith/actions/runs/5221379865. |
Perfect, that's all we need right now. I'll be merging this PR into the design-system-old and push a commit here when I have the new release version (Monday morning). Thanks! |
However for your purposes, this PR should also be able to tell you whether this fix truly does shave off the time or not. So, you shouldn't be blocked any longer, and this PR shouldn't be a blocker as to why the tests are failing. |
/build-deploy-preview |
Deploying Your Preview: https://github.com/appsmithorg/appsmith/actions/runs/5222241943. |
Deploy-Preview-URL: https://ce-23556.dp.appsmith.com |
/ok-to-test |
Tests running at: https://github.com/appsmithorg/appsmith/actions/runs/5240093603. |
/ok-to-test |
Tests running at: https://github.com/appsmithorg/appsmith/actions/runs/5240474843. |
I’m extremely puzzled why this CI in this branch suddenly caught much more ESLint errors than in |
Ah yes, e27e5ff bumped a bunch of versions in |
Ouch, that commit also seems to have broken a bunch of type checks (https://github.com/appsmithorg/appsmith/actions/runs/5242801442/jobs/9466660879). @SatishGandham I’m going to re-do the merge and erase your commit, apologies! |
859bb01
to
64242b8
Compare
Oh well. So due to palantir/blueprint#3248, removing the The root issue is here: This code gets executed when the bundle runs for the first time – and sets Sounds like we’ll have to figure out a different approach to prioritize the bundle without making it render-blocking. |
Superseded by #24374! |
Description
After #21933, the main bundle started to compete for bandwidth with the preloaded chunks. On pages without the preloads, the bundle would take ~2s to load (on 4G) (request no. 3):
whereas on pages with the chunks, it will take ~3s (request no. 13):
This PR fixes that by:
defer
attribute, which bumps the main bundle’s priority from Low to High (reference: https://addyosmani.com/blog/script-priorities/)fetchpriority="low"
on the preload chunksThis changes the waterfall to look like this instead:
Type of change
Testing
How Has This Been Tested?
Checklist:
Dev activity
QA activity:
Test Plan Approved
label after Cypress tests were reviewedTest Plan Approved
label after JUnit tests were reviewed