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 d2c7de203 fix(client): treat offline group connections as empty 
results (#4002)
d2c7de203 is described below

commit d2c7de203dce188ae319b7953c5b3f10c452ac70
Author: 烤化の初雪 <[email protected]>
AuthorDate: Mon Sep 7 17:35:48 2026 +0800

    fix(client): treat offline group connections as empty results (#4002)
    
    examineConsumerConnectionInfo answers MQClientException(206, "Not found
    the consumer group connection") for any group that is simply offline, so
    an all-offline cluster tripped the all-failed guard and GET /api/clients
    returned 502 while discarding the producer connections already collected
    in the same scan. The explicit producer-group lookup failed the same way
    for offline groups.
    
    Classify the offline-group answers as a normal empty outcome: count them
    as successful per-group results in the consumer scan and return an empty
    list for the explicit producer-group query. Genuine broker failures and
    partial scans keep their existing behavior.
    
    Co-authored-by: unbridled-41 
<[email protected]>
---
 .../provider/apache/RocketMQClientProvider.java    | 25 +++++++++++--
 .../apache/RocketMQClientProviderTest.java         | 43 ++++++++++++++++++++++
 2 files changed, 65 insertions(+), 3 deletions(-)

diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQClientProvider.java
 
b/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQClientProvider.java
index 237e4dce6..63b7cdcc5 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQClientProvider.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQClientProvider.java
@@ -36,6 +36,7 @@ import 
org.apache.rocketmq.studio.common.exception.BusinessException;
 import org.apache.rocketmq.studio.common.domain.enums.ClientLanguage;
 import org.apache.rocketmq.studio.common.domain.enums.ClientType;
 import org.apache.rocketmq.studio.common.domain.enums.Protocol;
+import org.apache.rocketmq.studio.common.util.MqResponseCodes;
 import org.apache.rocketmq.studio.common.util.SystemGroupFilter;
 import org.apache.rocketmq.tools.admin.MQAdminExt;
 import lombok.RequiredArgsConstructor;
@@ -171,9 +172,10 @@ public class RocketMQClientProvider implements 
ClientProvider {
                             connection, ClientType.Producer, topic, 
producerGroup, null))
                     .toList();
         } catch (MQClientException e) {
-            if (isTopicNotExist(e)) {
-                // A non-existent topic is a normal "nothing here" outcome — 
the client
-                // page should show an empty list, not a 502 that looks like a 
failure.
+            if (isTopicNotExist(e) || isGroupConnectionAbsent(e)) {
+                // A missing topic route or an offline producer group are 
normal "nothing
+                // here" outcomes — the client page should show an empty list, 
not a 502
+                // that looks like a failure.
                 return List.of();
             }
             throw new BusinessException(502,
@@ -184,6 +186,17 @@ public class RocketMQClientProvider implements 
ClientProvider {
         }
     }
 
+    private boolean isGroupConnectionAbsent(MQClientException e) {
+        if (MqResponseCodes.hasResponseCode(e, 
ResponseCode.CONSUMER_NOT_ONLINE)) {
+            // rocketmq-tools examineConsumerConnectionInfo throws 
CONSUMER_NOT_ONLINE (206)
+            // for a group with no online connections.
+            return true;
+        }
+        String message = e.getErrorMessage() == null ? e.getMessage() : 
e.getErrorMessage();
+        return message != null && (message.contains("Not found the consumer 
group connection")
+                || message.contains("Not found the producer group 
connection"));
+    }
+
     private boolean isTopicNotExist(MQClientException e) {
         if (e.getResponseCode() == ResponseCode.TOPIC_NOT_EXIST) {
             return true;
@@ -324,6 +337,12 @@ public class RocketMQClientProvider implements 
ClientProvider {
                             groupEntry.getValue()));
                 }
             } catch (Exception e) {
+                if (e instanceof MQClientException clientException && 
isGroupConnectionAbsent(clientException)) {
+                    // An offline group is a normal "no connections right now" 
answer, not a
+                    // broken scan — count it so an all-offline cluster stays 
an empty result.
+                    successfulGroupQueries++;
+                    continue;
+                }
                 log.warn("Failed to examine consumer connection for group={}, 
skipping", group, e);
             }
         }
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQClientProviderTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQClientProviderTest.java
index f23be04ca..a732b6ebd 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQClientProviderTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQClientProviderTest.java
@@ -504,6 +504,49 @@ class RocketMQClientProviderTest {
         });
     }
 
+    @Test
+    void consumerScanTreatsOfflineGroupsAsEmptyInsteadOf502() throws Exception 
{
+        SubscriptionGroupWrapper wrapper = subscriptionGroups("group-a", 
"group-b");
+        
when(adminExt.examineBrokerClusterInfo()).thenReturn(clusterInfo("127.0.0.1:10911"));
+        when(adminExt.getAllSubscriptionGroup("127.0.0.1:10911", 
5000L)).thenReturn(wrapper);
+        
when(adminExt.examineConsumerConnectionInfo(anyString())).thenThrow(new 
MQClientException(
+                206, "Not found the consumer group connection"));
+
+        List<ClientConnectionVO> connections = 
provider.findConnections("instance-a", "cluster-a", null);
+
+        assertThat(connections).isEmpty();
+    }
+
+    @Test
+    void consumerScanReturnsOfflineGroupResultsWhenAnotherGroupFails() throws 
Exception {
+        SubscriptionGroupWrapper wrapper = subscriptionGroups("group-a", 
"group-b");
+        ConsumerConnection consumerConnection = new ConsumerConnection();
+        consumerConnection.setConnectionSet(new 
HashSet<>(List.of(connection("consumer-client", "10.0.0.2:1000"))));
+        
when(adminExt.examineBrokerClusterInfo()).thenReturn(clusterInfo("127.0.0.1:10911"));
+        when(adminExt.getAllSubscriptionGroup("127.0.0.1:10911", 
5000L)).thenReturn(wrapper);
+        when(adminExt.examineConsumerConnectionInfo("group-a")).thenThrow(new 
MQClientException(
+                206, "Not found the consumer group connection"));
+        
when(adminExt.examineConsumerConnectionInfo("group-b")).thenReturn(consumerConnection);
+
+        List<ClientConnectionVO> connections = 
provider.findConnections("instance-a", "cluster-a", "Consumer");
+
+        assertThat(connections).singleElement().satisfies(connection -> {
+            assertThat(connection.getClientId()).isEqualTo("consumer-client");
+            assertThat(connection.getGroupOrTopic()).isEqualTo("group-b");
+        });
+    }
+
+    @Test
+    void producerQueryWithExplicitOfflineGroupReturnsEmptyInsteadOf502() 
throws Exception {
+        when(adminExt.examineProducerConnectionInfo("pg-order", 
"TopicA")).thenThrow(
+                new MQClientException("Not found the producer group 
connection", null));
+
+        List<ClientConnectionVO> connections =
+                provider.findProducerConnections("instance-a", "TopicA", 
"pg-order");
+
+        assertThat(connections).isEmpty();
+    }
+
     private static SubscriptionGroupWrapper subscriptionGroups(String... 
names) {
         SubscriptionGroupWrapper wrapper = new SubscriptionGroupWrapper();
         ConcurrentHashMap<String, SubscriptionGroupConfig> groups = new 
ConcurrentHashMap<>();

Reply via email to