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 5fd8b42be fix(cluster): preserve interrupts from pooled client actions 
(#6467)
5fd8b42be is described below

commit 5fd8b42be3f406bb6184e3b2684b372eedbb67dd
Author: Loyal-Young <[email protected]>
AuthorDate: Thu Oct 15 18:12:10 2026 +0800

    fix(cluster): preserve interrupts from pooled client actions (#6467)
---
 .../studio/cluster/broker/MqAdminExtFactory.java   |  3 ++
 .../studio/cluster/broker/MqClientPool.java        |  3 ++
 .../cluster/broker/MqAdminExtFactoryTest.java      | 36 +++++++++++++
 .../studio/cluster/broker/MqClientPoolTest.java    | 60 ++++++++++++++++++++++
 4 files changed, 102 insertions(+)

diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/cluster/broker/MqAdminExtFactory.java
 
b/server/src/main/java/org/apache/rocketmq/studio/cluster/broker/MqAdminExtFactory.java
index 5acdcc3e0..72b81eafa 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/cluster/broker/MqAdminExtFactory.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/cluster/broker/MqAdminExtFactory.java
@@ -131,6 +131,9 @@ public class MqAdminExtFactory {
         } catch (BusinessException ex) {
             throw ex;
         } catch (Exception ex) {
+            if (ex instanceof InterruptedException) {
+                Thread.currentThread().interrupt();
+            }
             log.warn("Admin action failed against namesrv {}: {}", 
namesrvAddr, ex.getMessage());
             throw new BusinessException(502, "RocketMQ admin call failed: " + 
rootMessage(ex));
         } finally {
diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/cluster/broker/MqClientPool.java
 
b/server/src/main/java/org/apache/rocketmq/studio/cluster/broker/MqClientPool.java
index e95bcda33..0ee8e3e96 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/cluster/broker/MqClientPool.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/cluster/broker/MqClientPool.java
@@ -122,6 +122,9 @@ public class MqClientPool {
         } catch (BusinessException ex) {
             throw ex;
         } catch (Exception ex) {
+            if (ex instanceof InterruptedException) {
+                Thread.currentThread().interrupt();
+            }
             log.warn("RocketMQ client action failed against namesrv {}: {}", 
cacheKey.namesrvAddr(), ex.getMessage());
             throw new BusinessException(502, "RocketMQ client call failed: " + 
rootMessage(ex));
         } finally {
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/cluster/broker/MqAdminExtFactoryTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/cluster/broker/MqAdminExtFactoryTest.java
index a1538e629..44b1a2784 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/cluster/broker/MqAdminExtFactoryTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/cluster/broker/MqAdminExtFactoryTest.java
@@ -54,6 +54,42 @@ class MqAdminExtFactoryTest {
         }
     }
 
+    @Test
+    void interruptedAdminActionShouldRestoreInterruptAndReleaseLeaseTest() {
+        DefaultMQAdminExt admin = mock(DefaultMQAdminExt.class);
+        RecordingFactory factory = new RecordingFactory(admin);
+        try {
+            assertThatThrownBy(() -> factory.execute("namesrv:9876", null, 
ignored -> {
+                Thread.currentThread().interrupt();
+                Thread.sleep(1);
+                return null;
+            })).isInstanceOf(BusinessException.class)
+                    .hasMessageContaining("RocketMQ admin call failed");
+            assertThat(Thread.currentThread().isInterrupted()).isTrue();
+        } finally {
+            Thread.interrupted();
+            factory.shutdown();
+        }
+        verify(admin).shutdown();
+    }
+
+    @Test
+    void ordinaryAdminFailureShouldNotSetInterruptTest() {
+        DefaultMQAdminExt admin = mock(DefaultMQAdminExt.class);
+        RecordingFactory factory = new RecordingFactory(admin);
+        try {
+            assertThatThrownBy(() -> factory.execute("namesrv:9876", null, 
ignored -> {
+                throw new IllegalStateException("admin failure");
+            })).isInstanceOf(BusinessException.class)
+                    .hasMessage("RocketMQ admin call failed: admin failure");
+            assertThat(Thread.currentThread().isInterrupted()).isFalse();
+        } finally {
+            Thread.interrupted();
+            factory.shutdown();
+        }
+        verify(admin).shutdown();
+    }
+
     @Test
     void executeShouldRejectBlankNamesrvAddr() {
         MqAdminExtFactory factory = new MqAdminExtFactory();
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/cluster/broker/MqClientPoolTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/cluster/broker/MqClientPoolTest.java
index ab6ac0f42..f8bbcc7e8 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/cluster/broker/MqClientPoolTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/cluster/broker/MqClientPoolTest.java
@@ -18,6 +18,7 @@ package org.apache.rocketmq.studio.cluster.broker;
 
 import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
 import org.apache.rocketmq.client.producer.DefaultMQProducer;
+import org.apache.rocketmq.studio.common.exception.BusinessException;
 import org.junit.jupiter.api.Test;
 import org.mockito.MockedConstruction;
 
@@ -26,12 +27,71 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.mockito.Mockito.mockConstruction;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
 class MqClientPoolTest {
 
+    @Test
+    void interruptedProducerActionShouldRestoreInterruptAndReleaseLeaseTest() {
+        try (MockedConstruction<DefaultMQProducer> construction = 
mockConstruction(DefaultMQProducer.class)) {
+            MqClientPool pool = new MqClientPool();
+            try {
+                assertThatThrownBy(() -> pool.withProducer("namesrv:9876", 
null, "credential-a", ignored -> {
+                    Thread.currentThread().interrupt();
+                    Thread.sleep(1);
+                    return null;
+                })).isInstanceOf(BusinessException.class)
+                        .hasMessageContaining("RocketMQ client call failed");
+                assertThat(Thread.currentThread().isInterrupted()).isTrue();
+            } finally {
+                Thread.interrupted();
+                pool.shutdown();
+            }
+            verify(construction.constructed().getFirst()).shutdown();
+        }
+    }
+
+    @Test
+    void interruptedPullActionShouldRestoreInterruptAndReleaseLeaseTest() {
+        try (MockedConstruction<DefaultMQPullConsumer> construction = 
mockConstruction(DefaultMQPullConsumer.class)) {
+            MqClientPool pool = new MqClientPool();
+            try {
+                assertThatThrownBy(() -> pool.withPullConsumer("namesrv:9876", 
null, "credential-a", ignored -> {
+                    Thread.currentThread().interrupt();
+                    Thread.sleep(1);
+                    return null;
+                })).isInstanceOf(BusinessException.class)
+                        .hasMessageContaining("RocketMQ client call failed");
+                assertThat(Thread.currentThread().isInterrupted()).isTrue();
+            } finally {
+                Thread.interrupted();
+                pool.shutdown();
+            }
+            verify(construction.constructed().getFirst()).shutdown();
+        }
+    }
+
+    @Test
+    void ordinaryProducerFailureShouldNotSetInterruptTest() {
+        try (MockedConstruction<DefaultMQProducer> construction = 
mockConstruction(DefaultMQProducer.class)) {
+            MqClientPool pool = new MqClientPool();
+            try {
+                assertThatThrownBy(() -> pool.withProducer("namesrv:9876", 
null, "credential-a", ignored -> {
+                    throw new IllegalStateException("producer failure");
+                })).isInstanceOf(BusinessException.class)
+                        .hasMessage("RocketMQ client call failed: producer 
failure");
+                assertThat(Thread.currentThread().isInterrupted()).isFalse();
+            } finally {
+                Thread.interrupted();
+                pool.shutdown();
+            }
+            verify(construction.constructed().getFirst()).shutdown();
+        }
+    }
+
     @Test
     void releaseShouldWaitForInFlightProducerBeforeShutdownTest() throws 
Exception {
         try (MockedConstruction<DefaultMQProducer> construction =

Reply via email to