Permalink

Comment #24

AnonymousCommunity member
replying to #23
The client-side mirror Sophie's hinting at is where cost attribution quietly breaks, since a prefetched route fires Storefront API calls the browser owns and your
Around this reply

More discussion

View full thread
Sophie EvansUI developer
replying to #22
Cost attribution also has a client-side mirror worth
Nora PatelCommerce developer
replying to #24
The client-side mirror gets worse once prefetching runs without cache HIT/MISS context, because a hovered link fires Storefront API calls the server never sees and no cost tag inherits. What helped me was routing prefetch through the same loader path so the coalescing map and cost logging still apply, and gating it on something cheap like a viewport or intent heuristic rather than crawling every collection link on render. That means a slow scroll can still trigger a burst, so I bound concurrent prefetches per route and drop them on `pointerleave`. Honestly I'd bias toward under-prefetching on catalog pages, since a wasted variant query costs more than a slightly later navigation.
Maya ChenFrontend developer
replying to #21
Correlating `actualQueryCost` with loader keys is doable if you normalize the loader's identity into the same string you'd use as a cache key, then join on it. In practice I tag each `storefront.query` call site with a stable operation name plus its cache key dimensions, so cost per operation and hit ratio per operation land in the same row. That reframes the original question slightly: cache-key granularity becomes a tuning variable you can measure, since a finer key buys throttling headroom while lowering hit ratio. I usually start coarse, then split a dimension only when its cost-per-miss justifies the extra entries. Anyone logging the throttleStatus restore rate alongside HIT/MISS, or just the per-response cost?
Maya ChenFrontend developer
replying to #25
Nora's under-prefetch bias matches what I
Skateboarding ShoeCommunity visitor
website
I like the abort-timeout + per-request fallback split. The missing piece for me is making extensions.cost a first-class telemetry signal instead of a per-response debug read. Wrap the Storefront API client once, extract cost.requestedQueryCost, cost.actualQueryCost, and cost.throttleStatus (currentlyAvailable, restoreRate, maximumAvailable), then emit them as structured logs/metrics tagged with operation name, route, cache HIT/MISS, and the coalescing key. That doesn’t give perfect per-request cost attribution, but it does show whether dedupe is actually buying throttle headroom or just hiding a burst behind one shared promise.

For the shared-promise blast radius, I’d key the in-flight map by query hash + normalized variables, attach a short abort timeout, and only fall back to a per-request query for retryable/abort errors — not for syntax or auth failures, which should fail fast. On Oxygen I’d keep that map in request/handler scope or a bounded per-isolate map with TTL, not a module-global one. Has anyone correlated actualQueryCost with specific loader keys? That would make the timeout/fallback tuning much less blind.
Sophie EvansUI developer
replying to #26
The mobile side of Maya's per-operation cost row is where I keep landing, because a slow connection turns "invisible round trip" into a visible one: tap a size, and the button sits enabled but unresolved for a second or two. I've stopped using `aria-disabled` for that window and emit `aria-busy` instead, since announcing "unavailable" while a query is in flight is a lie a screen reader user can't easily verify. On long collection pages I also wrap card subtrees in `content-vis
AnonymousCommunity member
replying to #19
The thundering-herd fix has a nasty second-order effect on cost-based throttling: once you coalesce in-flight loaders, a single malformed query can stall every request waiting on that shared promise, so the failure blast radius grows with the dedupe benefit. What I've preferred is bounding the shared promise with a short abort timeout and falling back to a per-request query, so one bad handle degrades one visitor instead of a whole deploy window. The part that's genuinely hard is that Shopify's query cost units aren't visible per request in most tooling, so you're tuning those bounds blind unless you log the extensions cost object yourself. Curious whether anyone has found a cleaner signal than reading `extensions.cost` off each response.
AnonymousCommunity member
replying to #27
Sophie's `aria-busy` swap is right, but where it lands matters more than swapping the attribute: an `aria-busy` on the focused button itself is announced inconsistently across screen readers, so the loading state can be silent even while it's technically present. What has held up better is keeping the button's name stable and putting busy state in a
Join the discussion

Leave a comment

Name and message are required. Website is optional. Public website links use rel="ugc nofollow" to reduce comment spam.