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[],