You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by zh...@apache.org on 2026/08/11 02:53:14 UTC

(shardingsphere) branch master updated: Throw instead of returning partial results when ExecutorEngine is interrupted (#39400)

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.git


The following commit(s) were added to refs/heads/master by this push:
     new 4d3d7267665 Throw instead of returning partial results when ExecutorEngine is interrupted (#39400)
4d3d7267665 is described below

commit 4d3d7267665d9da9e10fce8fca689e603e99e990
Author: ZIHAN DAI <99...@users.noreply.github.com>
AuthorDate: Tue Aug 11 12:53:08 2026 +1000

    Throw instead of returning partial results when ExecutorEngine is interrupted (#39400)
    
    getGroupResults restored the interrupt flag and then continued the loop, so
    every remaining Future.get() threw immediately and the method returned a result
    list silently missing one entry per interrupted group, reported as success.
    
    The sibling ExecutionException handler two lines below already terminates via
    throwException. Do the same after re-interrupting.
    
    Fixes #39399
---
 .../infra/executor/kernel/ExecutorEngine.java      |  1 +
 .../infra/executor/kernel/ExecutorEngineTest.java  | 36 ++++++++++++++++++++++
 2 files changed, 37 insertions(+)

diff --git a/infra/executor/src/main/java/org/apache/shardingsphere/infra/executor/kernel/ExecutorEngine.java b/infra/executor/src/main/java/org/apache/shardingsphere/infra/executor/kernel/ExecutorEngine.java
index a659d73f502..55d99d3a1cb 100644
--- a/infra/executor/src/main/java/org/apache/shardingsphere/infra/executor/kernel/ExecutorEngine.java
+++ b/infra/executor/src/main/java/org/apache/shardingsphere/infra/executor/kernel/ExecutorEngine.java
@@ -118,6 +118,7 @@ public final class ExecutorEngine implements AutoCloseable {
                 result.addAll(each.get());
             } catch (final InterruptedException ex) {
                 Thread.currentThread().interrupt();
+                return throwException(ex);
             } catch (final ExecutionException ex) {
                 return throwException(ex);
             }
diff --git a/infra/executor/src/test/java/org/apache/shardingsphere/infra/executor/kernel/ExecutorEngineTest.java b/infra/executor/src/test/java/org/apache/shardingsphere/infra/executor/kernel/ExecutorEngineTest.java
index 236ab235796..6d62ab9fab2 100644
--- a/infra/executor/src/test/java/org/apache/shardingsphere/infra/executor/kernel/ExecutorEngineTest.java
+++ b/infra/executor/src/test/java/org/apache/shardingsphere/infra/executor/kernel/ExecutorEngineTest.java
@@ -25,14 +25,20 @@ import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
+import org.apache.shardingsphere.infra.executor.kernel.model.ExecutorCallback;
+import org.apache.shardingsphere.infra.exception.generic.UnknownSQLException;
+
 import java.sql.SQLException;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.mockito.Mockito.mock;
 
 class ExecutorEngineTest {
@@ -88,4 +94,34 @@ class ExecutorEngineTest {
         latch.await();
         assertThat(actual.size(), is(4));
     }
+    
+    @Test
+    void assertParallelExecuteThrowsWhenInterrupted() throws InterruptedException {
+        CountDownLatch neverReleased = new CountDownLatch(1);
+        CountDownLatch asyncStarted = new CountDownLatch(1);
+        // The trunk thread interrupts itself while the async group is still running, so the
+        // Future.get() that follows throws InterruptedException. A future that has already
+        // completed hands back its value even with the flag set, hence the block.
+        ExecutorCallback<Object, String> interruptingFirstCallback = (inputs, isTrunkThread, processId) -> {
+            Thread.currentThread().interrupt();
+            return Collections.singletonList("succeed");
+        };
+        ExecutorCallback<Object, String> blockingCallback = (inputs, isTrunkThread, processId) -> {
+            asyncStarted.countDown();
+            try {
+                neverReleased.await(30L, TimeUnit.SECONDS);
+            } catch (final InterruptedException ignored) {
+                Thread.currentThread().interrupt();
+            }
+            return Collections.singletonList("succeed");
+        };
+        try {
+            assertThrows(UnknownSQLException.class,
+                    () -> executorEngine.execute(executionGroupContext, interruptingFirstCallback, blockingCallback, false));
+        } finally {
+            Thread.interrupted();
+            neverReleased.countDown();
+            asyncStarted.await(30L, TimeUnit.SECONDS);
+        }
+    }
 }