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 3a6611053 fix(dashboard): keep the overview inside the cluster the
instance is configured for (#6529)
3a6611053 is described below
commit 3a6611053496016debc1341f64f0430c0d042f9c
Author: Apulupie <[email protected]>
AuthorDate: Thu Oct 15 18:14:29 2026 +0800
fix(dashboard): keep the overview inside the cluster the instance is
configured for (#6529)
`786a308e` (#6196) applied the configured cluster to `refreshClusterDetail`
and
`discoverBrokers` and named the remaining topology readers: the dashboard
provider still
aggregated every cluster of a shared NameServer, so an instance configured
for cluster A
summed cluster B's brokers, topics, consumer groups and TPS into its
overview and per
cluster rows, unlike the cluster and broker pages it summarises.
`getDashboardData(String)` now resolves the configured cluster and
`collectDashboardData`
narrows both topology tables to it, keeping the configured cluster's
(possibly empty)
entry so a cluster missing from the topology degrades to a warning row the
way the
aggregate dashboard already handles unreachable instances.
Registered-instance and
legacy nameserver entry points are unchanged.
---
.../provider/apache/RocketMQDashboardProvider.java | 51 +++++++++++++--
.../apache/RocketMQDashboardProviderTest.java | 76 ++++++++++++++++++++++
2 files changed, 120 insertions(+), 7 deletions(-)
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQDashboardProvider.java
b/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQDashboardProvider.java
index 28117a306..5fdfd41a5 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQDashboardProvider.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQDashboardProvider.java
@@ -87,7 +87,8 @@ public class RocketMQDashboardProvider implements
DashboardProvider {
return unavailableTopologyDashboard();
}
return adminFactory.execute(namesrvAddr, null,
- admin -> collectDashboardData(admin,
ClusterType.V5_PROXY_CLUSTER, countEndpoints(namesrvAddr)));
+ admin -> collectDashboardData(admin,
ClusterType.V5_PROXY_CLUSTER,
+ countEndpoints(namesrvAddr), null));
}
return aggregateInstances(apacheInstances);
}
@@ -190,12 +191,14 @@ public class RocketMQDashboardProvider implements
DashboardProvider {
InstanceVO instance =
runtimeAdminClientResolver.resolveInstance(instanceId);
Integer configuredNameServers = instance.getType() ==
InstanceType.DIRECT
? countEndpoints(instance.getEndpoint()) : null;
+ String configuredCluster =
runtimeAdminClientResolver.configuredClusterName(instanceId);
return runtimeAdminClientResolver.execute(instance,
- admin -> collectDashboardData(admin, clusterTypeFor(instance),
configuredNameServers));
+ admin -> collectDashboardData(admin, clusterTypeFor(instance),
configuredNameServers,
+ configuredCluster));
}
private DashboardDataVO collectDashboardData(
- MQAdminExt admin, ClusterType clusterType, Integer
configuredNameServers) {
+ MQAdminExt admin, ClusterType clusterType, Integer
configuredNameServers, String configuredCluster) {
int totalClusters = 0;
int totalBrokers = 0;
int totalTopics = 0;
@@ -214,12 +217,14 @@ public class RocketMQDashboardProvider implements
DashboardProvider {
// The NameServer topology is decoded from JSON; a payload missing
either
// table yields null here. Treat it as empty so a partial topology
degrades
// instead of throwing and zeroing the whole dashboard.
- Map<String, Set<String>> clusterAddrTable =
+ Map<String, Set<String>> clusterAddrTable = scopeClusterTable(
clusterInfo.getClusterAddrTable() == null
- ? Map.of() : clusterInfo.getClusterAddrTable();
- Map<String, BrokerData> brokerAddrTable =
+ ? Map.of() : clusterInfo.getClusterAddrTable(),
+ configuredCluster);
+ Map<String, BrokerData> brokerAddrTable = scopeBrokerTable(
clusterInfo.getBrokerAddrTable() == null
- ? Map.of() : clusterInfo.getBrokerAddrTable();
+ ? Map.of() : clusterInfo.getBrokerAddrTable(),
+ clusterAddrTable, configuredCluster);
totalClusters = clusterAddrTable.size();
totalBrokers = brokerAddrTable.size();
@@ -447,6 +452,38 @@ public class RocketMQDashboardProvider implements
DashboardProvider {
.build();
}
+ /**
+ * Narrows the NameServer topology to the cluster a default-cluster
instance is configured for,
+ * the way {@code discoverClusters} and {@code discoverBrokers} already
narrow their reads: a
+ * shared NameServer must not mix another cluster's brokers, topics,
groups and TPS into this
+ * instance's dashboard. A configured cluster the topology does not
contain keeps its (empty)
+ * entry so the row degrades to a warning instead of disappearing.
+ */
+ private static Map<String, Set<String>> scopeClusterTable(
+ Map<String, Set<String>> clusterAddrTable, String
configuredCluster) {
+ if (configuredCluster == null) {
+ return clusterAddrTable;
+ }
+ Set<String> members = clusterAddrTable.get(configuredCluster);
+ return Map.of(configuredCluster, members == null ? Set.of() : members);
+ }
+
+ private static Map<String, BrokerData> scopeBrokerTable(
+ Map<String, BrokerData> brokerAddrTable, Map<String, Set<String>>
clusterAddrTable,
+ String configuredCluster) {
+ if (configuredCluster == null) {
+ return brokerAddrTable;
+ }
+ Map<String, BrokerData> scoped = new HashMap<>();
+ for (String brokerName :
clusterAddrTable.getOrDefault(configuredCluster, Set.of())) {
+ BrokerData brokerData = brokerAddrTable.get(brokerName);
+ if (brokerData != null) {
+ scoped.put(brokerName, brokerData);
+ }
+ }
+ return scoped;
+ }
+
private ClusterType clusterTypeFor(InstanceVO instance) {
return switch (instance.getType()) {
case DIRECT -> ClusterType.V4_DIRECT;
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQDashboardProviderTest.java
b/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQDashboardProviderTest.java
index e284a34f8..4e848406a 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQDashboardProviderTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQDashboardProviderTest.java
@@ -47,6 +47,7 @@ import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -433,6 +434,63 @@ class RocketMQDashboardProviderTest {
assertThat(dashboard.getStats().getTotalNameServers()).isEqualTo(2);
}
+ @Test
+ void
dashboardShouldNarrowTheTopologyToTheClusterConfiguredForTheInstanceTest()
throws Exception {
+ DefaultMQAdminExt adminExt = mock(DefaultMQAdminExt.class);
+ RuntimeAdminClientResolver resolver =
mock(RuntimeAdminClientResolver.class);
+ InstanceVO instance = InstanceVO.builder()
+ .type(InstanceType.PROXY_LOCAL)
+ .endpoint("local-proxy:8080")
+ .build();
+ instance.setId(1L);
+ when(resolver.resolveInstance("instance-local")).thenReturn(instance);
+
when(resolver.configuredClusterName("instance-local")).thenReturn("cluster-a");
+ when(resolver.execute(eq(instance), any())).thenAnswer(invocation ->
+
invocation.<MqAdminExtFactory.AdminAction<DashboardDataVO>>getArgument(1).apply(adminExt));
+
when(adminExt.examineBrokerClusterInfo()).thenReturn(clusterInfoWithTwoClusters());
+ when(adminExt.getAllTopicConfig(anyString(),
anyLong())).thenReturn(topicConfig("order-topic"));
+ when(adminExt.getAllSubscriptionGroup(anyString(),
anyLong())).thenReturn(subscriptionGroups());
+
when(adminExt.fetchBrokerRuntimeStats(anyString())).thenReturn(runtimeStats());
+
+ DashboardDataVO dashboard = newProvider(adminExt,
resolver).getDashboardData("instance-local");
+
+ assertThat(dashboard.getClusters()).singleElement().satisfies(cluster
-> {
+ assertThat(cluster.getName()).isEqualTo("cluster-a");
+ assertThat(cluster.getBrokers()).isEqualTo(1);
+ });
+ assertThat(dashboard.getStats().getTotalClusters()).isEqualTo(1);
+ assertThat(dashboard.getStats().getTotalBrokers()).isEqualTo(1);
+ verify(adminExt, never()).fetchBrokerRuntimeStats("10.0.0.12:10911");
+ verify(adminExt, never()).getAllTopicConfig(eq("10.0.0.12:10911"),
anyLong());
+ verify(adminExt,
never()).getAllSubscriptionGroup(eq("10.0.0.12:10911"), anyLong());
+ }
+
+ @Test
+ void
dashboardShouldExposeEveryClusterForAnInstanceWithoutAConfiguredClusterTest()
throws Exception {
+ DefaultMQAdminExt adminExt = mock(DefaultMQAdminExt.class);
+ RuntimeAdminClientResolver resolver =
mock(RuntimeAdminClientResolver.class);
+ InstanceVO instance = InstanceVO.builder()
+ .type(InstanceType.PROXY_LOCAL)
+ .endpoint("local-proxy:8080")
+ .build();
+ instance.setId(1L);
+ when(resolver.resolveInstance("instance-local")).thenReturn(instance);
+ when(resolver.execute(eq(instance), any())).thenAnswer(invocation ->
+
invocation.<MqAdminExtFactory.AdminAction<DashboardDataVO>>getArgument(1).apply(adminExt));
+
when(adminExt.examineBrokerClusterInfo()).thenReturn(clusterInfoWithTwoClusters());
+ when(adminExt.getAllTopicConfig(anyString(),
anyLong())).thenReturn(topicConfig("order-topic"));
+ when(adminExt.getAllSubscriptionGroup(anyString(),
anyLong())).thenReturn(subscriptionGroups());
+
when(adminExt.fetchBrokerRuntimeStats(anyString())).thenReturn(runtimeStats());
+
+ DashboardDataVO dashboard = newProvider(adminExt,
resolver).getDashboardData("instance-local");
+
+ assertThat(dashboard.getClusters()).extracting(cluster ->
cluster.getName())
+ .containsExactlyInAnyOrder("cluster-a", "cluster-b");
+ assertThat(dashboard.getStats().getTotalClusters()).isEqualTo(2);
+ assertThat(dashboard.getStats().getTotalBrokers()).isEqualTo(2);
+ verify(adminExt).fetchBrokerRuntimeStats("10.0.0.12:10911");
+ }
+
@Test
void dashboardShouldReportUnconfiguredLegacyTopologyAsUnavailable() {
RocketMQProperties properties = new RocketMQProperties();
@@ -838,6 +896,24 @@ class RocketMQDashboardProviderTest {
return new BrokerData("DefaultCluster", name, addresses);
}
+ private ClusterInfo clusterInfoWithTwoClusters() {
+ ClusterInfo info = new ClusterInfo();
+ HashMap<Long, String> firstAddresses = new HashMap<>();
+ firstAddresses.put(0L, "10.0.0.11:10911");
+ HashMap<Long, String> secondAddresses = new HashMap<>();
+ secondAddresses.put(0L, "10.0.0.12:10911");
+ HashMap<String, BrokerData> brokerAddrTable = new HashMap<>();
+ brokerAddrTable.put("broker-a", new BrokerData("cluster-a",
"broker-a", firstAddresses));
+ brokerAddrTable.put("broker-b", new BrokerData("cluster-b",
"broker-b", secondAddresses));
+ info.setBrokerAddrTable(brokerAddrTable);
+
+ HashMap<String, Set<String>> clusterAddrTable = new HashMap<>();
+ clusterAddrTable.put("cluster-a", Set.of("broker-a"));
+ clusterAddrTable.put("cluster-b", Set.of("broker-b"));
+ info.setClusterAddrTable(clusterAddrTable);
+ return info;
+ }
+
private TopicList topicList() {
TopicList topicList = new TopicList();
topicList.setTopicList(Set.of("order-topic"));