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

Myasuka pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink.git


The following commit(s) were added to refs/heads/master by this push:
     new e19fd025372 [FLINK-39984][runtime] Dispatch thread dump to ioExecutor
e19fd025372 is described below

commit e19fd025372b0437fe9414004a528420043ba0b0
Author: xingsuo-zbz <[email protected]>
AuthorDate: Fri Jul 24 12:52:35 2026 +0800

    [FLINK-39984][runtime] Dispatch thread dump to ioExecutor
    
    Offload the dump to ioExecutor in both TaskExecutor#requestThreadDump
    and Dispatcher#requestThreadDump via CompletableFuture.supplyAsync,
    matching the pattern already used by other heavy TE handlers.
---
 .../flink/runtime/dispatcher/Dispatcher.java       |  3 +-
 .../flink/runtime/taskexecutor/TaskExecutor.java   |  3 +-
 .../DispatcherThreadDumpOffloadTest.java           | 72 ++++++++++++++++
 .../runtime/taskexecutor/TaskExecutorBuilder.java  |  5 ++
 .../TaskExecutorThreadDumpOffloadTest.java         | 98 ++++++++++++++++++++++
 .../taskexecutor/TaskManagerServicesBuilder.java   |  9 +-
 6 files changed, 187 insertions(+), 3 deletions(-)

diff --git 
a/flink-runtime/src/main/java/org/apache/flink/runtime/dispatcher/Dispatcher.java
 
b/flink-runtime/src/main/java/org/apache/flink/runtime/dispatcher/Dispatcher.java
index 90f1f732b2a..50bb43285e0 100644
--- 
a/flink-runtime/src/main/java/org/apache/flink/runtime/dispatcher/Dispatcher.java
+++ 
b/flink-runtime/src/main/java/org/apache/flink/runtime/dispatcher/Dispatcher.java
@@ -1866,7 +1866,8 @@ public abstract class Dispatcher extends 
FencedRpcEndpoint<DispatcherId>
     @Override
     public CompletableFuture<ThreadDumpInfo> requestThreadDump(Duration 
timeout) {
         int stackTraceMaxDepth = 
configuration.get(ClusterOptions.THREAD_DUMP_STACKTRACE_MAX_DEPTH);
-        return 
CompletableFuture.completedFuture(ThreadDumpInfo.dumpAndCreate(stackTraceMaxDepth));
+        return CompletableFuture.supplyAsync(
+                () -> ThreadDumpInfo.dumpAndCreate(stackTraceMaxDepth), 
ioExecutor);
     }
 
     @Override
diff --git 
a/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/TaskExecutor.java
 
b/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/TaskExecutor.java
index 166935cbd8e..a7558bf6d3f 100644
--- 
a/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/TaskExecutor.java
+++ 
b/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/TaskExecutor.java
@@ -1471,7 +1471,8 @@ public class TaskExecutor extends RpcEndpoint implements 
TaskExecutorGateway {
                 taskManagerConfiguration
                         .getConfiguration()
                         .get(ClusterOptions.THREAD_DUMP_STACKTRACE_MAX_DEPTH);
-        return 
CompletableFuture.completedFuture(ThreadDumpInfo.dumpAndCreate(stacktraceMaxDepth));
+        return CompletableFuture.supplyAsync(
+                () -> ThreadDumpInfo.dumpAndCreate(stacktraceMaxDepth), 
ioExecutor);
     }
 
     @Override
diff --git 
a/flink-runtime/src/test/java/org/apache/flink/runtime/dispatcher/DispatcherThreadDumpOffloadTest.java
 
b/flink-runtime/src/test/java/org/apache/flink/runtime/dispatcher/DispatcherThreadDumpOffloadTest.java
new file mode 100644
index 00000000000..111e52a1414
--- /dev/null
+++ 
b/flink-runtime/src/test/java/org/apache/flink/runtime/dispatcher/DispatcherThreadDumpOffloadTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.flink.runtime.dispatcher;
+
+import org.apache.flink.runtime.rest.messages.ThreadDumpInfo;
+import org.apache.flink.runtime.rpc.RpcUtils;
+
+import org.junit.After;
+import org.junit.Test;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/** Tests that {@link Dispatcher#requestThreadDump} is offloaded to the 
ioExecutor. */
+public class DispatcherThreadDumpOffloadTest extends AbstractDispatcherTest {
+
+    private static final String IO_THREAD_NAME = "test-jm-io-thread";
+
+    private TestingDispatcher dispatcher;
+    private ExecutorService ioExecutor;
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        if (dispatcher != null) {
+            RpcUtils.terminateRpcEndpoint(dispatcher);
+        }
+        if (ioExecutor != null) {
+            ioExecutor.shutdownNow();
+        }
+        super.tearDown();
+    }
+
+    @Test
+    public void requestThreadDumpRunsOnIoExecutor() throws Exception {
+        // Named single-thread executor: if the dump runs on it, 
dumpAllThreads() captures
+        // the thread and it appears in the returned ThreadDumpInfo.
+        ioExecutor = Executors.newSingleThreadExecutor(r -> new Thread(r, 
IO_THREAD_NAME));
+
+        dispatcher = 
createTestingDispatcherBuilder().setIoExecutor(ioExecutor).build(rpcService);
+        dispatcher.start();
+
+        final ThreadDumpInfo dump =
+                dispatcher
+                        .getSelfGateway(DispatcherGateway.class)
+                        .requestThreadDump(TIMEOUT)
+                        .get(20, TimeUnit.SECONDS);
+
+        assertThat(dump.getThreadInfos())
+                .as("dump must include ioExecutor thread '%s' (proves 
offload)", IO_THREAD_NAME)
+                .anyMatch(t -> IO_THREAD_NAME.equals(t.getThreadName()));
+    }
+}
diff --git 
a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskExecutorBuilder.java
 
b/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskExecutorBuilder.java
index 73f0b7d6e9a..6c213f6cc71 100644
--- 
a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskExecutorBuilder.java
+++ 
b/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskExecutorBuilder.java
@@ -94,6 +94,11 @@ public class TaskExecutorBuilder {
         return this;
     }
 
+    public TaskExecutorBuilder setTaskManagerServices(TaskManagerServices 
taskManagerServices) {
+        this.taskManagerServices = taskManagerServices;
+        return this;
+    }
+
     public TaskExecutor build() throws Exception {
 
         final TaskExecutorBlobService resolvedTaskExecutorBlobService;
diff --git 
a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskExecutorThreadDumpOffloadTest.java
 
b/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskExecutorThreadDumpOffloadTest.java
new file mode 100644
index 00000000000..5257890563c
--- /dev/null
+++ 
b/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskExecutorThreadDumpOffloadTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.flink.runtime.taskexecutor;
+
+import org.apache.flink.core.testutils.EachCallbackWrapper;
+import org.apache.flink.runtime.entrypoint.WorkingDirectory;
+import 
org.apache.flink.runtime.highavailability.TestingHighAvailabilityServicesBuilder;
+import org.apache.flink.runtime.rest.messages.ThreadDumpInfo;
+import org.apache.flink.runtime.rpc.RpcUtils;
+import org.apache.flink.runtime.rpc.TestingRpcServiceExtension;
+import org.apache.flink.runtime.taskmanager.LocalUnresolvedTaskManagerLocation;
+import org.apache.flink.util.TestLoggerExtension;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.junit.jupiter.api.io.TempDir;
+
+import java.io.File;
+import java.time.Duration;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/** Tests that {@link TaskExecutor#requestThreadDump(Duration)} is offloaded 
to the ioExecutor. */
+@ExtendWith(TestLoggerExtension.class)
+class TaskExecutorThreadDumpOffloadTest {
+
+    private static final Duration RPC_TIMEOUT = Duration.ofSeconds(10);
+    private static final String IO_THREAD_NAME = "test-tm-io-thread";
+
+    private final TestingRpcServiceExtension rpcServiceExtension = new 
TestingRpcServiceExtension();
+
+    @RegisterExtension
+    private final EachCallbackWrapper<TestingRpcServiceExtension> eachWrapper =
+            new EachCallbackWrapper<>(rpcServiceExtension);
+
+    @Test
+    void requestThreadDumpRunsOnIoExecutor(@TempDir File tempDir) throws 
Exception {
+        // Named single-thread executor: if the dump runs on it, 
dumpAllThreads() captures
+        // the thread and it appears in the returned ThreadDumpInfo.
+        final ExecutorService ioExecutor =
+                Executors.newSingleThreadExecutor(r -> new Thread(r, 
IO_THREAD_NAME));
+        try {
+            final TaskManagerServices services =
+                    new TaskManagerServicesBuilder()
+                            .setUnresolvedTaskManagerLocation(
+                                    new LocalUnresolvedTaskManagerLocation())
+                            .setIoExecutor(ioExecutor)
+                            .build();
+
+            final TaskExecutor taskExecutor =
+                    TaskExecutorBuilder.newBuilder(
+                                    rpcServiceExtension.getTestingRpcService(),
+                                    new 
TestingHighAvailabilityServicesBuilder().build(),
+                                    WorkingDirectory.create(tempDir))
+                            .setTaskManagerServices(services)
+                            .build();
+            try {
+                taskExecutor.start();
+
+                final ThreadDumpInfo dump =
+                        taskExecutor
+                                .getSelfGateway(TaskExecutorGateway.class)
+                                .requestThreadDump(RPC_TIMEOUT)
+                                .get(20, TimeUnit.SECONDS);
+
+                assertThat(dump.getThreadInfos())
+                        .as(
+                                "dump must include ioExecutor thread '%s' 
(proves offload)",
+                                IO_THREAD_NAME)
+                        .anyMatch(t -> 
IO_THREAD_NAME.equals(t.getThreadName()));
+            } finally {
+                RpcUtils.terminateRpcEndpoint(taskExecutor);
+            }
+        } finally {
+            ioExecutor.shutdownNow();
+        }
+    }
+}
diff --git 
a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskManagerServicesBuilder.java
 
b/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskManagerServicesBuilder.java
index 68d9d5a275d..62b8ed470ae 100644
--- 
a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskManagerServicesBuilder.java
+++ 
b/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskManagerServicesBuilder.java
@@ -42,6 +42,7 @@ import 
org.apache.flink.runtime.taskmanager.UnresolvedTaskManagerLocation;
 import org.apache.flink.runtime.util.NoOpGroupCache;
 
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
 import static org.mockito.Mockito.mock;
@@ -68,6 +69,7 @@ public class TaskManagerServicesBuilder {
     private SharedResources sharedResources;
     private long managedMemorySize;
     private SlotAllocationSnapshotPersistenceService 
slotAllocationSnapshotPersistenceService;
+    private ExecutorService ioExecutor;
 
     public TaskManagerServicesBuilder() {
         unresolvedTaskManagerLocation = new 
LocalUnresolvedTaskManagerLocation();
@@ -181,6 +183,11 @@ public class TaskManagerServicesBuilder {
         return this;
     }
 
+    public TaskManagerServicesBuilder setIoExecutor(ExecutorService 
ioExecutor) {
+        this.ioExecutor = ioExecutor;
+        return this;
+    }
+
     public TaskManagerServices build() {
         return new TaskManagerServices(
                 unresolvedTaskManagerLocation,
@@ -197,7 +204,7 @@ public class TaskManagerServicesBuilder {
                 taskChangelogStoragesManager,
                 taskChannelStateExecutorFactoryManager,
                 taskEventDispatcher,
-                Executors.newSingleThreadScheduledExecutor(),
+                ioExecutor != null ? ioExecutor : 
Executors.newSingleThreadScheduledExecutor(),
                 libraryCacheManager,
                 slotAllocationSnapshotPersistenceService,
                 sharedResources,

Reply via email to