Cache GLQL dashboard results behind one shared Apollo client
What does this MR do and why?
Every GLQL request created its own ApolloClient with its own empty cache, and createDefaultClient() registered each one in apolloClients without ever removing it. So every panel, trend comparison and "next page" leaked a client, identical queries on different panels were fetched separately, and switching dashboard tabs or changing a filter refetched everything.
This MR adds a shared execution context per request queue. Executor.shared(queueName) returns one { client, results } per queue and endpoint path, so group and project namespaces stay separate. The client is a single ApolloClient with fetchPolicy: 'no-cache'; results is a new ResultCache keyed by compiled query text plus variables JSON.
Caching details:
- The cache stores the pending request, not just the result, so an identical in-flight request is joined instead of duplicated. Either way, one network request.
- A cached key (pending or finished) does not take a queue slot, and only the task that actually sends the request holds one.
- Failed requests are removed from the cache, so failures are never cached.
- At most 200 entries, LRU eviction, and entries expire 5 minutes after their request started so a long-open dashboard doesn't serve stale numbers.
- Apollo's
queryDeduplicationis disabled for dashboard requests, becauseforget()on reload drops entries for still-running requests and Apollo would otherwise return the pre-reload result. Embedded blocks keep deduplication.
Why not just one shared client with Apollo's InMemoryCache: GLQL analytics queries put metrics and GROUP BY dimensions inside nodes { ... } rather than in arguments, so panels sharing a date range write to the same normalized entry and the last writer wins. Worse, a panel whose selection set is a subset of another's can be served the other's rows as a silent cache hit and display a wrong number. Measured on the DAP Impact dashboard, that approach served only 2 of 12 Overview queries from cache.
Two related changes:
GlqlVisualization#reload()callsforget(...)to drop entries tagged with the panel's source, then emitsreload; the panel wrapper refetches and remounts the resolver, so no key bump is needed.retry()still bumps the key (nothing else changes on retry) after callingforget(...).extractGroupOrProjectnow strips the query string and hash, so explore dashboard URLs (no/-/) no longer derive a different endpoint path, and therefore a different context, on every tab switch.- Subquery variables (milestone/iteration lookups) now go through
#enqueue, so they are queued, cancelled and cached like the query they feed.
Measurements
api/glql requests counted in the browser network tab on the DAP Impact dashboard in GDK:
| Action | Before | After |
|---|---|---|
| First load of the Overview tab | 12 | 12 |
| Switch to Adoption tab | 12 | 12 |
| Switch back to Overview | 12 | 0 |
| Reload one panel | 2 | 2 |
References
- Parent work item, "Frontend caching by sharing one Apollo client": https://gitlab.com/gitlab-org/gitlab/-/work_items/630507
- Named queues this builds on: !256168 (merged)
- Skipping queued requests from unmounted resolvers: !257203 (merged)
- Trend comparisons alongside the main query: !257205 (merged)
- Readiness commitment keeping the default queue at concurrency 1: https://gitlab.com/gitlab-org/gitlab/-/issues/517546
Screenshots or screen recordings
Not applicable, no visible UI change, only which network requests are sent.
How to set up and validate locally
- Enable the
dap_impact_v1feature flag. - Open
/explore/analytics_dashboards, open the DAP Impact dashboard, pick a group, and filter the network tab toapi/glql. - Switch to another dashboard tab and back: no new requests.
- Change the date range: new requests. Change it back: no new requests.
- Panel actions menu > Reload: only that panel's queries are re-requested (two if it has a trend comparison).
- Leave the dashboard open for over five minutes, then switch tabs and back: panels request again.
- For contrast, an embedded GLQL block in an issue description still requests on every render.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.