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;

Reply via email to