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"));

Reply via email to