This is an automated email from the ASF dual-hosted git repository.

lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git


The following commit(s) were added to refs/heads/rocketmq-studio by this push:
     new abb0a7add fix(metrics): distinguish history scopes from literal 
instance names (#6403)
abb0a7add is described below

commit abb0a7addb1fe5b812114e9d942e4b9cdfd97500
Author: Chstercc <[email protected]>
AuthorDate: Thu Oct 15 18:09:46 2026 +0800

    fix(metrics): distinguish history scopes from literal instance names (#6403)
---
 web/src/utils/metricsExplorerDiagnostics.test.ts | 51 ++++++++++++++++++++++++
 web/src/utils/metricsExplorerDiagnostics.ts      | 16 ++++----
 2 files changed, 59 insertions(+), 8 deletions(-)

diff --git a/web/src/utils/metricsExplorerDiagnostics.test.ts 
b/web/src/utils/metricsExplorerDiagnostics.test.ts
index ee114df87..3488ddc42 100644
--- a/web/src/utils/metricsExplorerDiagnostics.test.ts
+++ b/web/src/utils/metricsExplorerDiagnostics.test.ts
@@ -86,6 +86,7 @@ const createHistoryEntry = (
     start: overrides.start ?? 1_799_996_400,
     end: overrides.end ?? 1_800_000_000,
     queriedAt: overrides.queriedAt ?? 1_800_000_000_000,
+    instanceId: overrides.instanceId,
     summary: overrides.summary ?? summarizeMetricData(metricData),
   });
 
@@ -282,6 +283,56 @@ describe('metrics explorer diagnostics', () => {
     expect(loadMetricsQueryHistory()).toEqual([]);
   });
 
+  it.each([false, true])(
+    'keeps unscoped history separate from an instance named global 
(reverse=%s) Test',
+    (reverse) => {
+      const unscoped = createHistoryEntry({ queriedAt: 1 });
+      const scoped = createHistoryEntry({ instanceId: 'global', queriedAt: 2 
});
+      const [existing, incoming] = reverse ? [scoped, unscoped] : [unscoped, 
scoped];
+
+      const merged = mergeMetricsQueryHistory([existing], incoming);
+
+      expect(merged).toEqual([incoming, existing]);
+      localStorage.clear();
+      expect(saveMetricsQueryHistory(merged)).toBe(true);
+      expect(loadMetricsQueryHistory()).toEqual([scoped, unscoped]);
+    },
+  );
+
+  it('deduplicates repeated queries within the literal global instance Test', 
() => {
+    const older = createHistoryEntry({ instanceId: 'global', queriedAt: 1 });
+    const newer = createHistoryEntry({ instanceId: 'global', queriedAt: 2 });
+
+    expect(mergeMetricsQueryHistory([older], newer)).toEqual([newer]);
+  });
+
+  it('deduplicates persisted legacy IDs using query fields Test', () => {
+    const legacy = {
+      ...createHistoryEntry({ instanceId: 'global', queriedAt: 1 }),
+      id: 'rocketmq5-native:message_in_tps:1h:default:global:1',
+    };
+    localStorage.clear();
+    saveMetricsQueryHistory([legacy]);
+    const newer = createHistoryEntry({ instanceId: 'global', queriedAt: 2 });
+
+    expect(mergeMetricsQueryHistory(loadMetricsQueryHistory(), 
newer)).toEqual([newer]);
+  });
+
+  it('gives unscoped and literal global queries different IDs at the same 
timestamp Test', () => {
+    const unscoped = createHistoryEntry({});
+    const scoped = createHistoryEntry({ instanceId: 'global' });
+
+    expect(scoped.id).not.toBe(unscoped.id);
+  });
+
+  it('distinguishes the default source from a source with the literal key 
default Test', () => {
+    const implicit = createHistoryEntry({ dataSourceKey: '' });
+    const explicit = createHistoryEntry({ dataSourceKey: 'default' });
+
+    expect(explicit.id).not.toBe(implicit.id);
+    expect(mergeMetricsQueryHistory([implicit], explicit)).toEqual([explicit, 
implicit]);
+  });
+
   // The unavailable-storage fallbacks are covered by browserStorage.test.ts, 
which is where
   // the try/catch now lives, so this only asserts the malformed-payload path.
   it('ignores malformed persisted history entries', () => {
diff --git a/web/src/utils/metricsExplorerDiagnostics.ts 
b/web/src/utils/metricsExplorerDiagnostics.ts
index e13235917..d8b18ffce 100644
--- a/web/src/utils/metricsExplorerDiagnostics.ts
+++ b/web/src/utils/metricsExplorerDiagnostics.ts
@@ -455,14 +455,14 @@ export const createMetricsQueryHistoryEntry = ({
   instanceId,
   summary,
 }: MetricsQueryHistoryInput): MetricsQueryHistoryEntry => ({
-  id: [
+  id: JSON.stringify([
     profileId,
     metric.semanticMetric,
     rangeId,
-    dataSourceKey || 'default',
-    instanceId || 'global',
+    dataSourceKey,
+    instanceId ?? null,
     queriedAt,
-  ].join(':'),
+  ]),
   profileId,
   profileName,
   metricId: metric.semanticMetric,
@@ -482,14 +482,14 @@ export const createMetricsQueryHistoryEntry = ({
 });
 
 const historyIdentity = (entry: MetricsQueryHistoryEntry) =>
-  [
+  JSON.stringify([
     entry.profileId,
     entry.metricId,
     entry.rangeId,
-    entry.dataSourceKey || 'default',
-    entry.instanceId || 'global',
+    entry.dataSourceKey,
+    entry.instanceId ?? null,
     entry.promql,
-  ].join('\n');
+  ]);
 
 export const mergeMetricsQueryHistory = (
   entries: MetricsQueryHistoryEntry[],

Reply via email to