This is an automated email from the ASF dual-hosted git repository.

terrymanu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere-elasticjob.git


The following commit(s) were added to refs/heads/master by this push:
     new 9c29b2222 Fixes registry center closed-client reads and nested 
interrupts (#2551)
9c29b2222 is described below

commit 9c29b2222c56b556ecc6f969549a12df63032580
Author: Hengqian Ling <[email protected]>
AuthorDate: Thu Oct 8 23:28:21 2026 +0800

    Fixes registry center closed-client reads and nested interrupts (#2551)
---
 RELEASE-NOTES.md                                   |  1 +
 .../reg/exception/RegExceptionHandler.java         |  2 +
 .../reg/exception/RegExceptionHandlerTest.java     | 13 +++
 .../reg/zookeeper/ZookeeperRegistryCenter.java     |  9 +++
 .../ZookeeperRegistryCenterClosedRaceTest.java     | 92 ++++++++++++++++++++++
 5 files changed, 117 insertions(+)

diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index a10096d53..59a663037 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -18,6 +18,7 @@
 1. Registry Center: Fixes the issue that the etcd registry center does not 
revoke the lease created by leader execution - 
[#2538](https://github.com/apache/shardingsphere-elasticjob/issues/2538)
 1. Registry Center: Fixes the issue that a closed `ZookeeperRegistryCenter` 
throws an exception instead of returning the not found value on read - 
[#2543](https://github.com/apache/shardingsphere-elasticjob/issues/2543)
 1. Registry Center: Fixes the issue that `ZookeeperRegistryCenter` throws a 
null pointer exception when watching a path without cache - 
[#2543](https://github.com/apache/shardingsphere-elasticjob/issues/2543)
+1. Registry Center: Fixes registry center closed-client reads and nested 
interrupts - 
[#2551](https://github.com/apache/shardingsphere-elasticjob/pull/2551)
 
 ## 3.0.5
 
diff --git 
a/registry-center/api/src/main/java/org/apache/shardingsphere/elasticjob/reg/exception/RegExceptionHandler.java
 
b/registry-center/api/src/main/java/org/apache/shardingsphere/elasticjob/reg/exception/RegExceptionHandler.java
index 42f59f8f6..67dce8920 100644
--- 
a/registry-center/api/src/main/java/org/apache/shardingsphere/elasticjob/reg/exception/RegExceptionHandler.java
+++ 
b/registry-center/api/src/main/java/org/apache/shardingsphere/elasticjob/reg/exception/RegExceptionHandler.java
@@ -42,6 +42,8 @@ public final class RegExceptionHandler {
             log.debug("Elastic job: ignored exception for: {}", 
cause.getMessage());
         } else if (cause instanceof InterruptedException) {
             Thread.currentThread().interrupt();
+        } else if (cause.getCause() instanceof InterruptedException) {
+            Thread.currentThread().interrupt();
         } else {
             throw new RegException(cause);
         }
diff --git 
a/registry-center/api/src/test/java/org/apache/shardingsphere/elasticjob/reg/exception/RegExceptionHandlerTest.java
 
b/registry-center/api/src/test/java/org/apache/shardingsphere/elasticjob/reg/exception/RegExceptionHandlerTest.java
index 0622e7c0b..9546336c6 100644
--- 
a/registry-center/api/src/test/java/org/apache/shardingsphere/elasticjob/reg/exception/RegExceptionHandlerTest.java
+++ 
b/registry-center/api/src/test/java/org/apache/shardingsphere/elasticjob/reg/exception/RegExceptionHandlerTest.java
@@ -20,7 +20,9 @@ package org.apache.shardingsphere.elasticjob.reg.exception;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 class RegExceptionHandlerTest {
     
@@ -36,6 +38,17 @@ class RegExceptionHandlerTest {
         RegExceptionHandler.handleException(null);
     }
     
+    @Test
+    void assertHandleExceptionWithNestedInterruptedException() {
+        Thread.interrupted();
+        try {
+            assertDoesNotThrow(() -> RegExceptionHandler.handleException(new 
RuntimeException(new InterruptedException())));
+            assertTrue(Thread.currentThread().isInterrupted());
+        } finally {
+            Thread.interrupted();
+        }
+    }
+    
     @Test
     void assertHandleExceptionWithOtherException() {
         assertThrows(RegException.class, () -> 
RegExceptionHandler.handleException(new RuntimeException()));
diff --git 
a/registry-center/provider/zookeeper-curator/src/main/java/org/apache/shardingsphere/elasticjob/reg/zookeeper/ZookeeperRegistryCenter.java
 
b/registry-center/provider/zookeeper-curator/src/main/java/org/apache/shardingsphere/elasticjob/reg/zookeeper/ZookeeperRegistryCenter.java
index 735d76ac1..c87abaf1d 100644
--- 
a/registry-center/provider/zookeeper-curator/src/main/java/org/apache/shardingsphere/elasticjob/reg/zookeeper/ZookeeperRegistryCenter.java
+++ 
b/registry-center/provider/zookeeper-curator/src/main/java/org/apache/shardingsphere/elasticjob/reg/zookeeper/ZookeeperRegistryCenter.java
@@ -197,6 +197,9 @@ public final class ZookeeperRegistryCenter implements 
CoordinatorRegistryCenter
             // CHECKSTYLE:OFF
         } catch (final Exception ex) {
             // CHECKSTYLE:ON
+            if (isClientClosed()) {
+                return null;
+            }
             RegExceptionHandler.handleException(ex);
             return null;
         }
@@ -214,6 +217,9 @@ public final class ZookeeperRegistryCenter implements 
CoordinatorRegistryCenter
             // CHECKSTYLE:OFF
         } catch (final Exception ex) {
             // CHECKSTYLE:ON
+            if (isClientClosed()) {
+                return Collections.emptyList();
+            }
             RegExceptionHandler.handleException(ex);
             return Collections.emptyList();
         }
@@ -247,6 +253,9 @@ public final class ZookeeperRegistryCenter implements 
CoordinatorRegistryCenter
             // CHECKSTYLE:OFF
         } catch (final Exception ex) {
             // CHECKSTYLE:ON
+            if (isClientClosed()) {
+                return false;
+            }
             RegExceptionHandler.handleException(ex);
             return false;
         }
diff --git 
a/registry-center/provider/zookeeper-curator/src/test/java/org/apache/shardingsphere/elasticjob/reg/zookeeper/ZookeeperRegistryCenterClosedRaceTest.java
 
b/registry-center/provider/zookeeper-curator/src/test/java/org/apache/shardingsphere/elasticjob/reg/zookeeper/ZookeeperRegistryCenterClosedRaceTest.java
new file mode 100644
index 000000000..b7f2abf19
--- /dev/null
+++ 
b/registry-center/provider/zookeeper-curator/src/test/java/org/apache/shardingsphere/elasticjob/reg/zookeeper/ZookeeperRegistryCenterClosedRaceTest.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.elasticjob.reg.zookeeper;
+
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.api.ExistsBuilder;
+import org.apache.curator.framework.api.GetChildrenBuilder;
+import org.apache.curator.framework.api.GetDataBuilder;
+import org.apache.curator.framework.imps.CuratorFrameworkState;
+import org.apache.shardingsphere.elasticjob.reg.exception.RegException;
+import org.apache.shardingsphere.elasticjob.test.util.ReflectionUtils;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class ZookeeperRegistryCenterClosedRaceTest {
+    
+    @Mock
+    private CuratorFramework client;
+    
+    @Mock
+    private GetDataBuilder getDataBuilder;
+    
+    @Mock
+    private GetChildrenBuilder getChildrenBuilder;
+    
+    @Mock
+    private ExistsBuilder existsBuilder;
+    
+    @Test
+    void assertGetDirectlyReturnsNullWhenClosedAfterCheck() throws Exception {
+        when(client.getState()).thenReturn(CuratorFrameworkState.STARTED, 
CuratorFrameworkState.STOPPED);
+        when(client.getData()).thenReturn(getDataBuilder);
+        when(getDataBuilder.forPath("/test")).thenThrow(new 
IllegalStateException("Client is not started or has been closed"));
+        assertNull(newRegCenter().getDirectly("/test"));
+    }
+    
+    @Test
+    void assertGetChildrenKeysReturnsEmptyWhenClosedAfterCheck() throws 
Exception {
+        when(client.getState()).thenReturn(CuratorFrameworkState.STARTED, 
CuratorFrameworkState.STOPPED);
+        when(client.getChildren()).thenReturn(getChildrenBuilder);
+        when(getChildrenBuilder.forPath("/test")).thenThrow(new 
IllegalStateException("Client is not started or has been closed"));
+        assertThat(newRegCenter().getChildrenKeys("/test").isEmpty(), 
is(true));
+    }
+    
+    @Test
+    void assertIsExistedReturnsFalseWhenClosedAfterCheck() throws Exception {
+        when(client.getState()).thenReturn(CuratorFrameworkState.STARTED, 
CuratorFrameworkState.STOPPED);
+        when(client.checkExists()).thenReturn(existsBuilder);
+        when(existsBuilder.forPath("/test")).thenThrow(new 
IllegalStateException("Client is not started or has been closed"));
+        assertFalse(newRegCenter().isExisted("/test"));
+    }
+    
+    @Test
+    void assertGetDirectlyStillThrowsWhenNotClosed() throws Exception {
+        when(client.getState()).thenReturn(CuratorFrameworkState.STARTED);
+        when(client.getData()).thenReturn(getDataBuilder);
+        when(getDataBuilder.forPath("/test")).thenThrow(new 
RuntimeException("boom"));
+        ZookeeperRegistryCenter regCenter = newRegCenter();
+        assertThrows(RegException.class, () -> regCenter.getDirectly("/test"));
+    }
+    
+    private ZookeeperRegistryCenter newRegCenter() {
+        ZookeeperRegistryCenter result = new ZookeeperRegistryCenter(null);
+        ReflectionUtils.setFieldValue(result, "client", client);
+        return result;
+    }
+}

Reply via email to