Dashboard resource-group call dedupe¶
Motivation¶
A live timing capture of the Dashboard first paint (browser Resource Timing API)
showed GET /api/arm/.../resource-groups being fetched twice on load, each a
~1–1.5 s ARM round trip competing for the single api sidecar. The two readers
used different React Query cache keys for the same upstream call, so TanStack
Query could not dedupe them:
- the
ResourcePicker"Workload RG" dropdown fetched under["arm-rgs", sub](inside an inline fetcher that maps rows to its ownItem[]shape); ClusterCardfetched under["arm", "resource-groups", sub]for the raw resource-group rows it needs for the provision duplicate-name guard.
User-facing change¶
No visible UI change. The duplicate resource-group request on Dashboard first paint is collapsed into a single shared fetch, removing one redundant ~1–1.5 s ARM round trip from the initial load contention.
Implementation¶
- New
web/src/api/resourceGroups.ts: pins ONE canonical query key (["arm", "resource-groups", sub]) + a sharedRESOURCE_GROUPS_STALE_MS(60 s) for the raw resource-group listing, and exposes an imperativefetchResourceGroups(queryClient, sub)that resolves through that key. ConfigBarandDashboardHeaderpicker fetchers now callfetchResourceGroupsfor the raw rows (then map toItem[]as before). Their picker OUTER key stays["arm-rgs", sub]on purpose — that entry caches the mappedItem[], which must not collide with ClusterCard's rawArmResourceGroup[]entry.ClusterCardnow references the canonical key + shared stale constant instead of an inline["arm", "resource-groups", sub]/60_000pair, so all three readers resolve the same cache entry and share one in-flight request.
API / IaC diff¶
None. Frontend-only React Query cache-key coordination; no backend route, contract, or Bicep change.
Validation¶
web/src/api/resourceGroups.test.ts(new, 4 tests): canonical key shape, concurrent-caller dedupe into one upstream call, cache hit within the stale window, and separate fetches per subscription.npx vitest runon the changed components + new helper: 28 + 4 tests pass.npm run build: clean (TypeScript + bundling).