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 a008d71a9 fix(observability): deduplicate bundled assets (#2224)
a008d71a9 is described below
commit a008d71a96c2898c3b721ba02b9227ffc1053bdd
Author: yyqdbngt <[email protected]>
AuthorDate: Wed Aug 19 14:26:12 2026 +0800
fix(observability): deduplicate bundled assets (#2224)
---
.../metrics/grafana/GrafanaDashboardService.java | 16 +++++++++++--
.../studio/ops/alert/AlertRuleAssetService.java | 19 ++++++++++++---
.../grafana/GrafanaDashboardServiceTest.java | 27 ++++++++++++++++++++++
.../ops/alert/AlertRuleAssetServiceTest.java | 25 ++++++++++++++++++++
4 files changed, 82 insertions(+), 5 deletions(-)
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/cluster/metrics/grafana/GrafanaDashboardService.java
b/server/src/main/java/org/apache/rocketmq/studio/cluster/metrics/grafana/GrafanaDashboardService.java
index c28bb5888..f20043e1f 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/cluster/metrics/grafana/GrafanaDashboardService.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/cluster/metrics/grafana/GrafanaDashboardService.java
@@ -31,6 +31,9 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
@@ -64,7 +67,7 @@ public class GrafanaDashboardService {
*/
public List<GrafanaDashboardInfo> listDashboards() {
List<GrafanaDashboardInfo> infos = new ArrayList<>();
- for (Resource resource : resolveResources()) {
+ for (Resource resource : resolveUniqueResources()) {
String uid = uidOf(resource);
if (uid == null) {
continue;
@@ -151,7 +154,7 @@ public class GrafanaDashboardService {
}
private Resource findResource(String uid) {
- for (Resource resource : resolveResources()) {
+ for (Resource resource : resolveUniqueResources()) {
if (uid.equals(uidOf(resource))) {
return resource;
}
@@ -159,6 +162,15 @@ public class GrafanaDashboardService {
return null;
}
+ private List<Resource> resolveUniqueResources() {
+ Map<String, Resource> resourcesByUid = new LinkedHashMap<>();
+ Arrays.stream(resolveResources())
+ .filter(resource -> uidOf(resource) != null)
+ .sorted(Comparator.comparing(Resource::getDescription))
+ .forEach(resource ->
resourcesByUid.putIfAbsent(uidOf(resource), resource));
+ return new ArrayList<>(resourcesByUid.values());
+ }
+
protected Resource[] resolveResources() {
try {
return resourceResolver.getResources(LOCATION_PATTERN);
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/ops/alert/AlertRuleAssetService.java
b/server/src/main/java/org/apache/rocketmq/studio/ops/alert/AlertRuleAssetService.java
index 85e0a5296..749918f5c 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/ops/alert/AlertRuleAssetService.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/ops/alert/AlertRuleAssetService.java
@@ -30,8 +30,12 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -61,7 +65,7 @@ public class AlertRuleAssetService {
*/
public List<AlertRuleAssetInfo> listAssets() {
List<AlertRuleAssetInfo> infos = new ArrayList<>();
- for (Resource resource : resolveResources()) {
+ for (Resource resource : resolveUniqueResources()) {
String name = nameOf(resource);
if (name == null) {
continue;
@@ -107,7 +111,7 @@ public class AlertRuleAssetService {
*/
public List<PrometheusAlertRule> loadDefaultRules() {
List<PrometheusAlertRule> rules = new ArrayList<>();
- for (Resource resource : resolveResources()) {
+ for (Resource resource : resolveUniqueResources()) {
if (nameOf(resource) == null) {
continue;
}
@@ -162,7 +166,7 @@ public class AlertRuleAssetService {
}
private Resource findResource(String name) {
- for (Resource resource : resolveResources()) {
+ for (Resource resource : resolveUniqueResources()) {
if (name.equals(nameOf(resource))) {
return resource;
}
@@ -170,6 +174,15 @@ public class AlertRuleAssetService {
return null;
}
+ private List<Resource> resolveUniqueResources() {
+ Map<String, Resource> resourcesByName = new LinkedHashMap<>();
+ Arrays.stream(resolveResources())
+ .filter(resource -> nameOf(resource) != null)
+ .sorted(Comparator.comparing(Resource::getDescription))
+ .forEach(resource ->
resourcesByName.putIfAbsent(nameOf(resource), resource));
+ return new ArrayList<>(resourcesByName.values());
+ }
+
protected Resource[] resolveResources() {
try {
return resourceResolver.getResources(LOCATION_PATTERN);
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/cluster/metrics/grafana/GrafanaDashboardServiceTest.java
b/server/src/test/java/org/apache/rocketmq/studio/cluster/metrics/grafana/GrafanaDashboardServiceTest.java
index b28644d5f..71554c556 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/cluster/metrics/grafana/GrafanaDashboardServiceTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/cluster/metrics/grafana/GrafanaDashboardServiceTest.java
@@ -161,6 +161,24 @@ class GrafanaDashboardServiceTest {
assertTrue(exception.getMessage().contains("resolve bundled Grafana
dashboards"));
}
+ @Test
+ void dashboardOperationsShouldDeterministicallyDeduplicateUid() throws
Exception {
+ GrafanaDashboardService service = serviceWithResources(
+ resource("duplicate.json", "z-location",
"{\"title\":\"Second\"}"),
+ resource("duplicate.json", "a-location",
"{\"title\":\"First\"}"));
+
+ assertEquals(List.of(new GrafanaDashboardInfo("duplicate", "First",
"", List.of())),
+ service.listDashboards());
+ assertEquals("First", service.getDashboard("duplicate").get("title"));
+
+ try (ZipInputStream zip = new ZipInputStream(
+ new ByteArrayInputStream(service.getDashboardsArchive()),
StandardCharsets.UTF_8)) {
+ assertEquals("duplicate.json", zip.getNextEntry().getName());
+ assertTrue(new String(zip.readAllBytes(),
StandardCharsets.UTF_8).contains("First"));
+ assertNull(zip.getNextEntry());
+ }
+ }
+
private static GrafanaDashboardService serviceWithResources(Resource...
resources) {
return new GrafanaDashboardService(new ObjectMapper()) {
@Override
@@ -171,11 +189,20 @@ class GrafanaDashboardServiceTest {
}
private static Resource resource(String filename, String content) {
+ return resource(filename, filename, content);
+ }
+
+ private static Resource resource(String filename, String description,
String content) {
return new ByteArrayResource(content.getBytes(StandardCharsets.UTF_8))
{
@Override
public String getFilename() {
return filename;
}
+
+ @Override
+ public String getDescription() {
+ return description;
+ }
};
}
}
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/ops/alert/AlertRuleAssetServiceTest.java
b/server/src/test/java/org/apache/rocketmq/studio/ops/alert/AlertRuleAssetServiceTest.java
index f60aae581..664bbe800 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/ops/alert/AlertRuleAssetServiceTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/ops/alert/AlertRuleAssetServiceTest.java
@@ -132,6 +132,22 @@ class AlertRuleAssetServiceTest {
"warning", "broker", "BrokerDown", "")),
service.loadDefaultRules());
}
+ @Test
+ void assetOperationsShouldDeterministicallyDeduplicateName() {
+ AlertRuleAssetService service = serviceWithResources(
+ resource("duplicate.yaml", "z-location", "groups:\n - name:
second\n rules:\n"
+ + " - alert: SecondRule\n expr: up ==
2\n"),
+ resource("duplicate.yaml", "a-location", "groups:\n - name:
first\n rules:\n"
+ + " - alert: FirstRule\n expr: up ==
1\n"));
+
+ assertEquals(List.of(new AlertRuleAssetInfo("duplicate", "first", 1,
List.of("warning"))),
+ service.listAssets());
+ assertEquals(List.of("FirstRule"), service.loadDefaultRules().stream()
+ .map(PrometheusAlertRule::alert)
+ .toList());
+ assertTrue(service.getAssetYaml("duplicate").contains("FirstRule"));
+ }
+
private static AlertRuleAssetService serviceWithResources(Resource...
resources) {
return new AlertRuleAssetService() {
@Override
@@ -142,11 +158,20 @@ class AlertRuleAssetServiceTest {
}
private static Resource resource(String filename, String content) {
+ return resource(filename, filename, content);
+ }
+
+ private static Resource resource(String filename, String description,
String content) {
return new ByteArrayResource(content.getBytes(StandardCharsets.UTF_8))
{
@Override
public String getFilename() {
return filename;
}
+
+ @Override
+ public String getDescription() {
+ return description;
+ }
};
}
}