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 ccdb01599 fix(message): grade an offline direct-consume client as not
found (#4605)
ccdb01599 is described below
commit ccdb01599b1c2102ba158843c0b0b3565ebb124d
Author: 烤化の初雪 <[email protected]>
AuthorDate: Sat Sep 26 18:46:16 2026 +0800
fix(message): grade an offline direct-consume client as not found (#4605)
The broker answers SYSTEM_ERROR with "The Consumer <group> <client> not
online"
when the typed client id is not connected, and the raw MQClientException was
graded as a gateway failure, so POST /api/messages/direct-consume answered
502
with the raw broker text. The consumer stack endpoint already treats the
same
answer as the business state it is; direct consume now matches it and keeps
other broker failures as 502.
---
.../provider/apache/RocketMQMessageProvider.java | 26 +++++++++++++++++--
.../apache/RocketMQMessageProviderTest.java | 29 ++++++++++++++++++++++
2 files changed, 53 insertions(+), 2 deletions(-)
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQMessageProvider.java
b/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQMessageProvider.java
index 1f3e276eb..66e052932 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQMessageProvider.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQMessageProvider.java
@@ -573,8 +573,21 @@ public class RocketMQMessageProvider implements
MessageProvider {
||
!target.masters().contains(host.getAddress().getHostAddress() + ":" +
host.getPort())) {
throw new BusinessException(409, "Message's store
broker is not in the target master set");
}
- var result = ((DefaultMQAdminExt)
admin).consumeMessageDirectly(group, client,
- topic, message.getMsgId());
+
org.apache.rocketmq.remoting.protocol.body.ConsumeMessageDirectlyResult result;
+ try {
+ result = ((DefaultMQAdminExt)
admin).consumeMessageDirectly(group, client,
+ topic, message.getMsgId());
+ } catch (Exception exception) {
+ String rootMessage = rootMessage(exception);
+ // The broker answers SYSTEM_ERROR with "The Consumer
<group> <client> not online"
+ // when the client is not connected. That is a
business state the operator can act
+ // on, not a gateway failure, so it is graded like the
consumer stack endpoint does.
+ if (rootMessage != null && rootMessage.contains("not
online")) {
+ throw new BusinessException(404, "Consumer client
is not online: " + client
+ + " (group " + group + ")");
+ }
+ throw new BusinessException(502, "Failed to consume
message directly: " + rootMessage);
+ }
return DirectConsumeMessageResultVO.builder()
.consumeResult(result.getConsumeResult() == null ?
"UNKNOWN" : result.getConsumeResult().name())
.remark(result.getRemark()).spentTimeMillis(result.getSpentTimeMills())
@@ -944,6 +957,15 @@ public class RocketMQMessageProvider implements
MessageProvider {
return index < fields.length ? fields[index] : "";
}
+ private static String rootMessage(Throwable error) {
+ Throwable current = error;
+ while (current.getCause() != null && current.getCause() != current) {
+ current = current.getCause();
+ }
+ String message = current.getMessage();
+ return message == null || message.isBlank() ?
current.getClass().getSimpleName() : message;
+ }
+
private static long parseLong(String value) {
try {
return Long.parseLong(value.trim());
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQMessageProviderTest.java
b/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQMessageProviderTest.java
index c7961e5f6..87494e274 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQMessageProviderTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQMessageProviderTest.java
@@ -246,6 +246,35 @@ class RocketMQMessageProviderTest {
return request;
}
+ @Test
+ void directlyConsumesMessageReportsAnOfflineClientAsNotFoundTest() throws
Exception {
+ // The broker answers SYSTEM_ERROR with "The Consumer <group> <client>
not online" when
+ // the typed client id is not connected, which is the normal state for
a stale dialog.
+ MessageExt message = prepareDirectConsumption();
+ when(adminExt.consumeMessageDirectly("billing", "client-offline",
"orders", message.getMsgId()))
+ .thenThrow(new MQClientException(ResponseCode.SYSTEM_ERROR,
+ "The Consumer billing client-offline not online"));
+ DirectConsumeMessageDTO request = directRequest();
+ request.setClientId("client-offline");
+
+ assertThatThrownBy(() -> provider.consumeMessageDirectly(request))
+ .isInstanceOf(BusinessException.class)
+ .hasMessageContaining("Consumer client is not online:
client-offline")
+ .satisfies(error -> assertThat(((BusinessException)
error).getCode()).isEqualTo(404));
+ }
+
+ @Test
+ void directlyConsumesMessageKeepsOtherBrokerFailuresAsBadGatewayTest()
throws Exception {
+ MessageExt message = prepareDirectConsumption();
+ when(adminExt.consumeMessageDirectly("billing", "client-a", "orders",
message.getMsgId()))
+ .thenThrow(new MQClientException(ResponseCode.SYSTEM_ERROR,
"broker rejected the request"));
+
+ assertThatThrownBy(() ->
provider.consumeMessageDirectly(directRequest()))
+ .isInstanceOf(BusinessException.class)
+ .hasMessageContaining("Failed to consume message directly")
+ .satisfies(error -> assertThat(((BusinessException)
error).getCode()).isEqualTo(502));
+ }
+
@Test
void queryMessagesShouldRejectInvertedTimeRangeBeforeAdminLookup() throws
Exception {
assertThatThrownBy(() -> provider.queryMessages(