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 855cb5087 fix(cluster): order discovered clusters by name (#6388)
855cb5087 is described below

commit 855cb5087da1b4d0f7652fed922c48557f5e458f
Author: 0 <[email protected]>
AuthorDate: Thu Oct 15 20:19:03 2026 +0800

    fix(cluster): order discovered clusters by name (#6388)
    
    * fix(cluster): order discovered clusters by name
    
    * fix(cluster): list a cluster brokers in name order and align the rationale
    
    * test(cluster): pin broker name order and drop the fully-qualified TreeSet
    
    The broker half of the discovery ordering fix shipped without coverage:
    reverting `new TreeSet<>(brokerNames)` to the raw set iteration left the
    whole suite green, because every existing fixture's broker names happened
    to iterate in name order. Add a case whose LinkedHashSet is filled in
    reverse name order, so the assertion can only hold when buildBrokerList
    walks a name-sorted copy.
    
    Also use the short `TreeSet` name: the file already imports it and the two
    sibling call sites (discoverBrokers and the proxy-ip walk) use the short
    form, so the qualified name was an inconsistency rather than a choice.
    
    ---------
    
    Co-authored-by: lizhimins <[email protected]>
---
 .../provider/apache/RocketMQClusterProvider.java   | 11 +++-
 .../apache/RocketMQClusterProviderTest.java        | 62 ++++++++++++++++++++++
 2 files changed, 71 insertions(+), 2 deletions(-)

diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQClusterProvider.java
 
b/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQClusterProvider.java
index c21495703..3eb1df2ee 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQClusterProvider.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQClusterProvider.java
@@ -112,7 +112,12 @@ public class RocketMQClusterProvider implements 
ClusterProvider {
 
 
                 List<ClusterVO> clusters = new ArrayList<>();
-                for (Map.Entry<String, Set<String>> entry : 
clusterAddrTable.entrySet()) {
+                // A HashMap iterates deterministically for a given key set, 
but nothing agrees
+                // on that order across the providers; sort by cluster name so 
the discovered list
+                // matches the ordering RealClusterProvider already applies to 
its own table walk.
+                List<Map.Entry<String, Set<String>>> entries = new 
ArrayList<>(clusterAddrTable.entrySet());
+                entries.sort(Map.Entry.comparingByKey());
+                for (Map.Entry<String, Set<String>> entry : entries) {
                     String clusterName = entry.getKey();
                     Set<String> brokerNames = entry.getValue();
 
@@ -242,7 +247,9 @@ public class RocketMQClusterProvider implements 
ClusterProvider {
             return brokers;
         }
 
-        for (String brokerName : brokerNames) {
+        // brokerNames is a deserialised HashSet, so its iteration order is 
hash order while
+        // RealClusterProvider lists a cluster's brokers by name; walk a 
sorted copy so the views agree.
+        for (String brokerName : new TreeSet<>(brokerNames)) {
             BrokerData brokerData = brokerAddrTable.get(brokerName);
             if (brokerData == null || brokerData.getBrokerAddrs() == null) {
                 continue;
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQClusterProviderTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQClusterProviderTest.java
index 54bed71ab..58acf901a 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQClusterProviderTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQClusterProviderTest.java
@@ -24,6 +24,7 @@ import org.apache.rocketmq.remoting.protocol.body.Connection;
 import org.apache.rocketmq.remoting.protocol.body.ConsumerConnection;
 import org.apache.rocketmq.remoting.protocol.body.KVTable;
 import org.apache.rocketmq.remoting.protocol.route.BrokerData;
+import org.apache.rocketmq.studio.cluster.broker.BrokerVO;
 import org.apache.rocketmq.studio.cluster.broker.ClusterVO;
 import org.apache.rocketmq.studio.cluster.broker.MqAdminExtFactory;
 import org.apache.rocketmq.studio.cluster.broker.MqAdminProperties;
@@ -41,6 +42,7 @@ import org.junit.jupiter.params.provider.ValueSource;
 
 import java.util.HashMap;
 import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -359,6 +361,66 @@ class RocketMQClusterProviderTest {
                 .isEqualTo("10.0.3.5:8080");
     }
 
+    @Test
+    void discoverClustersShouldOrderClustersByNameRegardlessOfTableOrderTest() 
throws Exception {
+        DefaultMQAdminExt adminExt = mock(DefaultMQAdminExt.class);
+        RocketMQClusterProvider provider = newProvider(adminExt);
+
+        ClusterInfo clusterInfo = new ClusterInfo();
+        HashMap<Long, String> addrs = new HashMap<>();
+        addrs.put(0L, "10.0.0.11:10911");
+        HashMap<String, BrokerData> brokerAddrTable = new HashMap<>();
+        brokerAddrTable.put("broker-a", new BrokerData("zeta-cluster", 
"broker-a", addrs));
+        HashMap<String, Set<String>> clusterAddrTable = new LinkedHashMap<>();
+        // Inserted in reverse order so the table's own iteration order 
differs from name order.
+        clusterAddrTable.put("zeta-cluster", Set.of("broker-a"));
+        clusterAddrTable.put("alpha-cluster", Set.of("broker-a"));
+        clusterInfo.setBrokerAddrTable(brokerAddrTable);
+        clusterInfo.setClusterAddrTable(clusterAddrTable);
+        when(adminExt.examineBrokerClusterInfo()).thenReturn(clusterInfo);
+        
when(adminExt.fetchBrokerRuntimeStats(anyString())).thenReturn(runtimeStats());
+
+        List<ClusterVO> clusters = provider.discoverClusters();
+
+        // RealClusterProvider already returns clusters sorted by name; the 
instance-backed
+        // discovery must agree, so both views list the same clusters in the 
same order.
+        assertThat(clusters).extracting(ClusterVO::getId)
+                .containsExactly("alpha-cluster", "zeta-cluster");
+    }
+
+    @Test
+    void discoverClustersShouldOrderBrokersByNameRegardlessOfSetOrderTest() 
throws Exception {
+        DefaultMQAdminExt adminExt = mock(DefaultMQAdminExt.class);
+        RocketMQClusterProvider provider = newProvider(adminExt);
+
+        HashMap<Long, String> firstAddrs = new HashMap<>();
+        firstAddrs.put(0L, "10.0.0.11:10911");
+        HashMap<Long, String> secondAddrs = new HashMap<>();
+        secondAddrs.put(0L, "10.0.0.12:10911");
+        HashMap<String, BrokerData> brokerAddrTable = new HashMap<>();
+        brokerAddrTable.put("broker-a", new BrokerData("DefaultCluster", 
"broker-a", firstAddrs));
+        brokerAddrTable.put("broker-b", new BrokerData("DefaultCluster", 
"broker-b", secondAddrs));
+        // A LinkedHashSet keeps this insertion order, which is the reverse of 
name order, so the
+        // assertion below can only hold when buildBrokerList walks a 
name-sorted copy.
+        Set<String> brokerNames = new LinkedHashSet<>(List.of("broker-b", 
"broker-a"));
+        HashMap<String, Set<String>> clusterAddrTable = new LinkedHashMap<>();
+        clusterAddrTable.put("DefaultCluster", brokerNames);
+        ClusterInfo clusterInfo = new ClusterInfo();
+        clusterInfo.setBrokerAddrTable(brokerAddrTable);
+        clusterInfo.setClusterAddrTable(clusterAddrTable);
+        when(adminExt.examineBrokerClusterInfo()).thenReturn(clusterInfo);
+        
when(adminExt.fetchBrokerRuntimeStats(anyString())).thenReturn(runtimeStats());
+
+        List<ClusterVO> clusters = provider.discoverClusters();
+
+        // RealClusterProvider lists a cluster's brokers by name; the 
instance-backed discovery
+        // must agree, so both views render the same broker order.
+        assertThat(clusters).hasSize(1);
+        assertThat(clusters.get(0).getBrokers())
+                .extracting(BrokerVO::getName)
+                .containsExactly("broker-a", "broker-b");
+    }
+
     private RocketMQClusterProvider newProvider(DefaultMQAdminExt adminExt) {
         MqAdminExtFactory adminFactory = mock(MqAdminExtFactory.class);
         when(adminFactory.execute(anyString(), any(), 
any())).thenAnswer(invocation ->

Reply via email to