MWPW-200568 Parallelize initService script loading in merch block#6296
MWPW-200568 Parallelize initService script loading in merch block#6296TsayAdobe wants to merge 7 commits into
Conversation
|
@TsayAdobe thank you very much for this contribution 🚀 |
|
@vhargrave I also converted |
Axelcureno
left a comment
There was a problem hiding this comment.
Nice win on the waterfall — the loadScript swap and the mep-utils dynamic import both look right.
One thing to fix before merge: with geo detection on, getLocaleSettings and getValidatedMarket aren't actually independent. The sequential await was priming the akamai cache for the second call. In Promise.all they both hit geo2.adobe.com — I reproduced it locally, 1 fetch on stage vs 2 on this branch. Details inline.
On tests
There are none here, and the existing ones can't catch this. Every geo test either pre-seeds sessionStorage.akamai (merch.test.js:285, market.test.js:172/217/235/273) or stubs fetch to reject — so getCountry short-circuits at utils.js:952 and never hits the network. The suite only exercises the cache-hit path, which is exactly what the sequential await guaranteed and this PR removes.
Something like this fails on the branch, passes on stage:
it('does not double-fetch geo when geo detection is on', async () => {
sessionStorage.removeItem('akamai'); // force the network path
const fetchStub = sinon.stub(window, 'fetch');
fetchStub.withArgs(sinon.match(/geo2\.adobe\.com/)).resolves({
ok: true, json: () => Promise.resolve({ country: 'DE' }),
});
// set mas-geo-detection meta to 'on'
await initService(true);
expect(fetchStub.withArgs(sinon.match(/geo2\.adobe\.com/)).callCount).to.equal(1);
});Also worth a countryFromMarket assertion with the cache cold, and a fragment-client test that initService still resolves when loadScript rejects — that .catch() is load-bearing now (without it one failed script rejects the whole Promise.all) and has no coverage today.
| log?.error('Failed to load fragment-client.js:', e); | ||
| } | ||
| } | ||
| const [, , { language, locale, country }, validatedMarket] = await Promise.all([ |
There was a problem hiding this comment.
These two look independent but aren't. Old order:
await getLocaleSettings() → getCountry() → getAkamaiCode() → writes sessionStorage.akamai
await getValidatedMarket() → getCountry() → reads it ✅
The sequential await was doing load-bearing work — priming the cache before the second read. In parallel, both call getCountry() before either has written it. getAkamaiCode (geo.js:7) has no in-flight dedup and is cache: 'no-cache', so nothing collapses them.
I ran this locally against the branch — sequential gives 1 geo2.adobe.com fetch, parallel gives 2. So: two concurrent geo fetches per geo-enabled page load. Related risk I did not reproduce, but the code path is there — getValidatedMarket() resolving on a null country falls back to currLang.defaultMarket || 'us' (market.js:38), which would be a wrong-country price render on a slow or failing geo call.
Keeps most of the win — chain just the market call off the settings that prime the cache:
const localeSettingsPromise = getLocaleSettings(miloLocale);
const [, , { language, locale, country }, validatedMarket] = await Promise.all([
loadMasComponent(COMMERCE_LIBRARY),
fragmentClientUrl ? loadScript(fragmentClientUrl, 'module').catch(...) : Promise.resolve(),
localeSettingsPromise,
useGeoMarket
? localeSettingsPromise.then(() => import('../../utils/market.js')
.then(({ getValidatedMarket }) => getValidatedMarket()))
: Promise.resolve(null),
]);loadMasComponent / loadScript / getLocaleSettings are genuinely independent, so that's where the real gain was anyway. Alternative: memoize the in-flight promise in getAkamaiCode — fixes the double-fetch for every caller, since getCountry and resolveDetectedMarketCountry both funnel through it.
|
@vhargrave you're right that this PR alone won't move the needle much — so we dug into where the ~2s actually goes. We traced the full load path and measured plans.html live. TL;DR: the data fetches are fast (40–140ms each); the time is serialized orchestration. On the measured page, the first merch data request can't fire until ~2.9s, then takes 60ms. Where the time goes (measured, warm cache):
What MAS team is fixing (tickets filed):
Two small asks of Milo core (block-level code runs too late for both):
Projection: MAS fixes alone get cards to ~3s; with the two hooks, ~1.8–2.2s — within ~0.5s of the personalization floor, which is the practical best for any block on a personalized page. Longer-term ideas (parallel section loading with ordered reveal, scoped personalization) would help every block, not just merch — happy to share the full RCA and measurements if there's interest. cc @Blainegunn @sukamat @TsayAdobe @reil-adobe @3ch023 @npeltier |
|
This pull request is not passing all required checks. Please see this discussion for information on how to get all checks passing. Inconsistent checks can be manually retried. If a test absolutely can not pass for a good reason, please add a comment with an explanation to the PR. |
|
I added static hints for https://www.adobe.com/fr/creativecloud.html https://www.adobe.com/fr/products/photosho.html cc @Blainegunn @sukamat @vhargrave @Axelcureno @reil-adobe @3ch023 @npeltier |




Resolves: MWPW-200568
Test URLs:
Change on https://www.adobe.com/fr/creativecloud.html with content overrides

Before:
After
