This is an automated email from the ASF dual-hosted git repository.
wu-sheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-horizon-ui.git
The following commit(s) were added to refs/heads/main by this push:
new 09f996b ui: pre-fetch instance lists for every service in landing
09f996b is described below
commit 09f996be994ef2fe19594d879f6a7be3122f186b
Author: Wu Sheng <[email protected]>
AuthorDate: Sun May 17 19:22:49 2026 +0800
ui: pre-fetch instance lists for every service in landing
Closing the slow-service-switch gap on layers like so11y_java_agent.
Service-list cache (staleTime: Infinity) and dashboard-config bundle
mean the operator already gets the service picker + widget layout
instantly on layer entry, but the instance list per service was
still fetched on demand — clicking a different service stalled
~100-500ms on the BFF instance round-trip before the dashboard
query could even fire.
New `usePrefetchLayerInstances(layerKey, serviceIds)` composable
walks the landing's service ids and fires `prefetchQuery` for each
under the SAME cache key as the active `useLayerInstances`
(plain values in the key so the cache slots align). LayerDashboardsView
calls it once landingRows arrive, so by the time the operator
clicks a different service its instance list is already in
vue-query cache — picker populates instantly, dashboard query
fires immediately.
Bumped the instance staleTime 30s → 60s so the prefetched copy
stays usable for the typical "browse a few services" window. The
dashboard widget batch itself still has to fetch fresh per
(service, instance) — that's the actual OAP MQE evaluation and
can't be pre-cached (data is time-sensitive).
---
apps/ui/src/layer/useLayerInstances.ts | 51 ++++++++++++++++++++--
.../render/layer-dashboard/LayerDashboardsView.vue | 13 +++++-
2 files changed, 60 insertions(+), 4 deletions(-)
diff --git a/apps/ui/src/layer/useLayerInstances.ts
b/apps/ui/src/layer/useLayerInstances.ts
index 2f7977a..7238b19 100644
--- a/apps/ui/src/layer/useLayerInstances.ts
+++ b/apps/ui/src/layer/useLayerInstances.ts
@@ -22,16 +22,18 @@
* BFF would reject as `missing_service`.
*/
-import { computed, type Ref } from 'vue';
-import { useQuery } from '@tanstack/vue-query';
+import { computed, watch, type Ref } from 'vue';
+import { useQuery, useQueryClient } from '@tanstack/vue-query';
import { bffClient } from '@/api/client';
+const INSTANCES_STALE_MS = 60_000;
+
export function useLayerInstances(layerKey: Ref<string>, service: Ref<string |
null>) {
const q = useQuery({
queryKey: ['layer-instances', layerKey, service],
queryFn: () => bffClient.layer.instances(layerKey.value, service.value ??
''),
enabled: computed(() => layerKey.value.length > 0 && !!service.value),
- staleTime: 30_000,
+ staleTime: INSTANCES_STALE_MS,
});
return {
data: computed(() => q.data.value ?? null),
@@ -41,3 +43,46 @@ export function useLayerInstances(layerKey: Ref<string>,
service: Ref<string | n
error: q.error,
};
}
+
+/**
+ * Background pre-fetch of instance lists for every service in the
+ * supplied list. Run after the landing rows arrive so the operator's
+ * very next service-switch click finds the new service's instance
+ * list already in cache — eliminating the ~100-500ms BFF round-trip
+ * that otherwise gates the dashboard refire.
+ *
+ * Uses vue-query's `prefetchQuery` so each fetch goes through the
+ * SAME cache key as `useLayerInstances`. The active picker call
+ * then hits the cache instantly with no duplicate request. Skips
+ * services we already have cached + not stale.
+ *
+ * Idempotent (queryClient.prefetchQuery dedups in-flight requests
+ * by key) and tied to the calling scope's watch so it re-runs when
+ * the layer or service list changes (e.g. when the operator
+ * navigates to a different layer or hits the refresh button).
+ */
+export function usePrefetchLayerInstances(
+ layerKey: Ref<string>,
+ serviceIds: Ref<ReadonlyArray<string>>,
+): void {
+ const qc = useQueryClient();
+ watch(
+ [layerKey, serviceIds],
+ ([key, ids]) => {
+ if (!key) return;
+ for (const id of ids) {
+ if (!id) continue;
+ // Plain values in the key — vue-query serialises refs by
+ // their resolved .value, so this hashes to the same slot as
+ // the active `useLayerInstances` queryKey for the same
+ // (layerKey, id) pair.
+ void qc.prefetchQuery({
+ queryKey: ['layer-instances', key, id],
+ queryFn: () => bffClient.layer.instances(key, id),
+ staleTime: INSTANCES_STALE_MS,
+ });
+ }
+ },
+ { immediate: true },
+ );
+}
diff --git a/apps/ui/src/render/layer-dashboard/LayerDashboardsView.vue
b/apps/ui/src/render/layer-dashboard/LayerDashboardsView.vue
index 246ba7f..23e97ab 100644
--- a/apps/ui/src/render/layer-dashboard/LayerDashboardsView.vue
+++ b/apps/ui/src/render/layer-dashboard/LayerDashboardsView.vue
@@ -35,7 +35,7 @@ import { colorForMetric } from '@/utils/metricColor';
import { useLayerDashboard, useLayerDashboardConfig } from
'@/render/layer-dashboard/useLayerDashboard';
import { useLayerPageOrchestrator } from
'@/render/layer-dashboard/useLayerPageOrchestrator';
import { useLayerEndpoints } from '@/layer/useLayerEndpoints';
-import { useLayerInstances } from '@/layer/useLayerInstances';
+import { useLayerInstances, usePrefetchLayerInstances } from
'@/layer/useLayerInstances';
import { useLayerLanding } from '@/layer/useLayerLanding';
import { useTimeRangeStore } from '@/controls/timeRange';
import { useLayers } from '@/shell/useLayers';
@@ -148,6 +148,17 @@ const expandedInstance = ref<string | null>(null);
* bound to it. */
const { setSelected: setSelectedService } = useSelectedService();
const landingRows = computed(() => landing.data.value?.sampledRows ??
landing.rows.value ?? []);
+// Background pre-fetch — kick off instance-list requests for every
+// service in the landing as soon as rows arrive, so the operator's
+// next service-switch click finds the new service's instance list
+// already cached. Uses vue-query's prefetchQuery under the same
+// cache key as the active `useLayerInstances`, so no duplicate
+// requests fire. Eliminates the ~100-500ms BFF round-trip that
+// otherwise gates the dashboard refire on service change.
+usePrefetchLayerInstances(
+ layerKey,
+ computed(() => landingRows.value.map((r) => r.serviceId)),
+);
watch(landingRows, (rows) => {
const first = rows[0];
if (!first) return;