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 c86831cc3 fix(metadata): return empty results for topics without a
broker route (#3346)
c86831cc3 is described below
commit c86831cc3c61e345cfa8ea869f5bb43f1796045a
Author: 烤化の初雪 <[email protected]>
AuthorDate: Tue Sep 8 15:53:08 2026 +0800
fix(metadata): return empty results for topics without a broker route
(#3346)
Co-authored-by: unbridled-41
<[email protected]>
---
.../provider/apache/RocketMQMetadataProvider.java | 19 ++++++++
.../apache/RocketMQMetadataProviderTest.java | 56 ++++++++++++++++++++++
2 files changed, 75 insertions(+)
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQMetadataProvider.java
b/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQMetadataProvider.java
index 6d77fc91d..4b93fee68 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQMetadataProvider.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQMetadataProvider.java
@@ -22,6 +22,7 @@ import org.apache.rocketmq.common.constant.PermName;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.TopicConfig;
+import org.apache.rocketmq.remoting.protocol.ResponseCode;
import org.apache.rocketmq.remoting.protocol.admin.ConsumeStats;
import org.apache.rocketmq.remoting.protocol.admin.OffsetWrapper;
import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
@@ -40,6 +41,7 @@ import
org.apache.rocketmq.studio.common.domain.enums.ConsumeType;
import org.apache.rocketmq.studio.common.domain.enums.SubscriptionMode;
import org.apache.rocketmq.studio.common.domain.enums.TopicPerm;
import org.apache.rocketmq.studio.common.util.Pagination;
+import org.apache.rocketmq.studio.common.util.MqResponseCodes;
import org.apache.rocketmq.studio.common.util.SubscriptionFilterModes;
import org.apache.rocketmq.studio.common.util.SystemGroupFilter;
import org.apache.rocketmq.studio.common.util.SystemTopicFilter;
@@ -447,6 +449,12 @@ public class RocketMQMetadataProvider implements
MetadataProvider {
}
return routes;
} catch (Exception e) {
+ if (MqResponseCodes.hasResponseCode(e,
ResponseCode.TOPIC_NOT_EXIST)) {
+ // A record created in the metadata database without a broker
route is a
+ // normal "not synced yet" state — surface an empty route
list, not a 502.
+ log.info("Topic {} has no broker route yet: {}", name,
e.getMessage());
+ return Collections.emptyList();
+ }
log.warn("Failed to get routes for topic {}: {}", name,
e.getMessage());
throw new BusinessException(502, "Failed to get routes for topic "
+ name + ": " + e.getMessage());
}
@@ -574,6 +582,17 @@ public class RocketMQMetadataProvider implements
MetadataProvider {
.pageSize(pageSize)
.build();
} catch (Exception e) {
+ if (MqResponseCodes.hasResponseCode(e,
ResponseCode.TOPIC_NOT_EXIST)) {
+ // Same as routes: a metadata record without a broker route is
a normal
+ // "not synced yet" state, so the consumer page comes back
empty.
+ log.info("Topic {} has no broker route yet: {}", name,
e.getMessage());
+ return TopicConsumerPageVO.builder()
+ .items(List.of())
+ .total(0)
+ .page(page)
+ .pageSize(pageSize)
+ .build();
+ }
log.warn("Failed to get consumers for topic {}: {}", name,
e.getMessage());
throw new BusinessException(502, "Failed to get consumers for
topic " + name + ": " + e.getMessage());
}
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQMetadataProviderTest.java
b/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQMetadataProviderTest.java
index 4bb63e688..729ca200a 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQMetadataProviderTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQMetadataProviderTest.java
@@ -27,6 +27,8 @@ import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.protocol.admin.ConsumeStats;
import org.apache.rocketmq.remoting.protocol.admin.OffsetWrapper;
import org.apache.rocketmq.remoting.protocol.body.GroupList;
+import org.apache.rocketmq.client.exception.MQClientException;
+import org.apache.rocketmq.remoting.protocol.ResponseCode;
import org.apache.rocketmq.remoting.protocol.route.BrokerData;
import org.apache.rocketmq.remoting.protocol.route.QueueData;
import org.apache.rocketmq.remoting.protocol.route.TopicRouteData;
@@ -411,6 +413,60 @@ class RocketMQMetadataProviderTest {
.satisfies(error -> assertThat(((BusinessException)
error).getCode()).isEqualTo(502));
}
+ @Test
+ void getTopicRoutesShouldReturnEmptyListWhenTopicHasNoBrokerRoute() throws
Exception {
+ DefaultMQAdminExt admin =
org.mockito.Mockito.mock(DefaultMQAdminExt.class);
+ // Exception shape captured against a live RocketMQ 5.5.0 name server:
+ // MQClientException(responseCode=17, errorMessage="No topic route
info in name
+ // server for the topic: <topic>").
+ when(admin.examineTopicRouteInfo("TopicA")).thenThrow(new
MQClientException(
+ ResponseCode.TOPIC_NOT_EXIST,
+ "No topic route info in name server for the topic: TopicA"));
+
+ assertThat(newLiveProvider(admin).getTopicRoutes(null,
"TopicA")).isEmpty();
+ }
+
+ @Test
+ void getTopicRoutesGradesByResponseCodeOnly() throws Exception {
+ DefaultMQAdminExt admin =
org.mockito.Mockito.mock(DefaultMQAdminExt.class);
+ // A failure carrying route-absent-looking text but a different
response code is
+ // a real error and must surface, proving grading no longer keys on
the message.
+ when(admin.examineTopicRouteInfo("TopicA")).thenThrow(new
MQClientException(
+ ResponseCode.SYSTEM_ERROR,
+ "No topic route info in name server for the topic: TopicA"));
+
+ assertThatThrownBy(() -> newLiveProvider(admin).getTopicRoutes(null,
"TopicA"))
+ .isInstanceOf(BusinessException.class)
+ .satisfies(error -> assertThat(((BusinessException)
error).getCode()).isEqualTo(502));
+ }
+
+ @Test
+ void getTopicConsumersShouldReturnEmptyPageWhenTopicHasNoBrokerRoute()
throws Exception {
+ DefaultMQAdminExt admin =
org.mockito.Mockito.mock(DefaultMQAdminExt.class);
+ when(admin.queryTopicConsumeByWho("TopicA")).thenThrow(new
MQClientException(
+ ResponseCode.TOPIC_NOT_EXIST,
+ "No topic route info in name server for the topic: TopicA"));
+
+ TopicConsumerPageVO page =
newLiveProvider(admin).getTopicConsumersPage(null, "TopicA", 1, 20);
+
+ assertThat(page.getItems()).isEmpty();
+ assertThat(page.getTotal()).isZero();
+ assertThat(page.getPage()).isEqualTo(1);
+ assertThat(page.getPageSize()).isEqualTo(20);
+ }
+
+ @Test
+ void getTopicConsumersGradesByResponseCodeOnly() throws Exception {
+ DefaultMQAdminExt admin =
org.mockito.Mockito.mock(DefaultMQAdminExt.class);
+ when(admin.queryTopicConsumeByWho("TopicA")).thenThrow(new
MQClientException(
+ ResponseCode.SYSTEM_ERROR,
+ "No topic route info in name server for the topic: TopicA"));
+
+ assertThatThrownBy(() ->
newLiveProvider(admin).getTopicConsumersPage(null, "TopicA", 1, 20))
+ .isInstanceOf(BusinessException.class)
+ .satisfies(error -> assertThat(((BusinessException)
error).getCode()).isEqualTo(502));
+ }
+
@Test
void getTopicConsumersSurfacesAdminFailure() throws Exception {
DefaultMQAdminExt admin =
org.mockito.Mockito.mock(DefaultMQAdminExt.class);