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 f81e73a49 fix(cluster): dedupe name-server rows in views (#5774)
f81e73a49 is described below
commit f81e73a49a7ab776fbc8d71e0d0f05956fc276cc
Author: 0 <[email protected]>
AuthorDate: Mon Oct 19 18:40:06 2026 +0800
fix(cluster): dedupe name-server rows in views (#5774)
Verified against `rocketmq-studio` at b88aedc34 before merging:
缺陷仍在(`cluster/broker/RealClusterProvider.java:150-153` 仍只 trim+filter 无
`distinct`;`RocketMQClusterProvider.buildNameServerList:431-448` 仍无去重,被 `:125`
merge-tree clean against the current trunk. Merged as-is (squash) per the
maintainer review of 2026-10-19.
Follow-up (non-blocking): the new test hand-rolls a factory instead of
reusing the `newProvider` helper.
---
.../studio/cluster/broker/RealClusterProvider.java | 3 +++
.../provider/apache/RocketMQClusterProvider.java | 6 +++++-
.../cluster/broker/RealClusterProviderTest.java | 12 ++++++++++++
.../apache/RocketMQClusterProviderTest.java | 21 +++++++++++++++++++++
4 files changed, 41 insertions(+), 1 deletion(-)
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/cluster/broker/RealClusterProvider.java
b/server/src/main/java/org/apache/rocketmq/studio/cluster/broker/RealClusterProvider.java
index be27a4d86..b2942c859 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/cluster/broker/RealClusterProvider.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/cluster/broker/RealClusterProvider.java
@@ -150,6 +150,9 @@ public class RealClusterProvider implements ClusterProvider
{
List<NameServerVO> nameServers =
Arrays.stream(namesrvAddr.split("[;,]"))
.map(String::trim)
.filter(addr -> !addr.isEmpty())
+ // The admin client deduplicates the endpoint before
connecting; the reported
+ // rows must match that view instead of repeating a node per
repeated segment.
+ .distinct()
.map(addr ->
NameServerVO.builder().addr(addr).status(ClusterStatus.healthy).build())
.toList();
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 3eb1df2ee..ca6070929 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
@@ -42,6 +42,7 @@ import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -435,9 +436,12 @@ public class RocketMQClusterProvider implements
ClusterProvider {
}
String[] addrs = namesrvAddr.split("[;,]");
+ Set<String> seen = new LinkedHashSet<>();
for (String addr : addrs) {
String trimmed = addr.trim();
- if (!trimmed.isEmpty()) {
+ if (!trimmed.isEmpty() && seen.add(trimmed)) {
+ // Deduplicate: the admin connection treats a repeated segment
as one node, so
+ // the reported rows must not repeat it either.
nameServers.add(NameServerVO.builder()
.addr(trimmed)
.status(ClusterStatus.healthy)
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/cluster/broker/RealClusterProviderTest.java
b/server/src/test/java/org/apache/rocketmq/studio/cluster/broker/RealClusterProviderTest.java
index 82b5a7d75..2b2a9d2b0 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/cluster/broker/RealClusterProviderTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/cluster/broker/RealClusterProviderTest.java
@@ -109,6 +109,18 @@ class RealClusterProviderTest {
assertThat(cluster.getBrokers().get(0).getStatus()).isEqualTo(BrokerStatus.running);
}
+ @Test
+ void describeClusterShouldDeduplicateRepeatedNameServerSegments() throws
Exception {
+ stubClusterInfo("10.0.0.1:9876;10.0.0.2:9876;10.0.0.1:9876",
sampleClusterInfo());
+
+ ClusterVO cluster =
provider.describeCluster("10.0.0.1:9876;10.0.0.2:9876;10.0.0.1:9876");
+
+ // The admin client deduplicates the endpoint segments before
connecting; the reported
+ // name-server rows must not repeat a node the connection itself
treats once.
+ assertThat(cluster.getNameServers()).extracting("addr")
+ .containsExactly("10.0.0.1:9876", "10.0.0.2:9876");
+ }
+
@Test
void describeClusterShouldDefaultAbsentRuntimeCollectionsToEmpty() throws
Exception {
stubClusterInfo("10.0.0.1:9876", sampleClusterInfo());
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 58acf901a..aeb7270f9 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
@@ -29,6 +29,7 @@ import org.apache.rocketmq.studio.cluster.broker.ClusterVO;
import org.apache.rocketmq.studio.cluster.broker.MqAdminExtFactory;
import org.apache.rocketmq.studio.cluster.broker.MqAdminProperties;
import org.apache.rocketmq.studio.cluster.broker.RuntimeAdminClientResolver;
+import org.apache.rocketmq.studio.cluster.nameserver.NameServerVO;
import org.apache.rocketmq.studio.common.domain.enums.ClusterStatus;
import org.apache.rocketmq.studio.common.domain.enums.InstanceType;
import org.apache.rocketmq.studio.common.domain.enums.InstanceVendor;
@@ -62,6 +63,26 @@ import static org.mockito.Mockito.when;
class RocketMQClusterProviderTest {
+ @Test
+ void discoverClustersShouldDeduplicateRepeatedNameServerSegments() throws
Exception {
+ DefaultMQAdminExt adminExt = mock(DefaultMQAdminExt.class);
+ RocketMQProperties properties = new RocketMQProperties();
+ properties.setNamesrvAddr("10.0.0.1:9876;10.0.0.2:9876;10.0.0.1:9876");
+ MqAdminExtFactory adminFactory = mock(MqAdminExtFactory.class);
+ when(adminFactory.execute(anyString(), any(),
any())).thenAnswer(invocation ->
+
invocation.<MqAdminExtFactory.AdminAction<Object>>getArgument(2).apply(adminExt));
+ RocketMQClusterProvider provider =
+ new RocketMQClusterProvider(adminFactory, properties,
mock(RuntimeAdminClientResolver.class));
+
+ when(adminExt.examineBrokerClusterInfo()).thenReturn(clusterInfo());
+
+ List<ClusterVO> clusters = provider.discoverClusters();
+
+ // The admin connection deduplicates the endpoint before use; the
reported rows must too.
+
assertThat(clusters.get(0).getNameServers()).extracting(NameServerVO::getAddr)
+ .containsExactly("10.0.0.1:9876", "10.0.0.2:9876");
+ }
+
@Test
void discoverClustersShouldParseRuntimeTpsWithExtraWhitespace() throws
Exception {
DefaultMQAdminExt adminExt = mock(DefaultMQAdminExt.class);