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

MartijnVisser 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 cbd7792ee9b [FLINK-38536][tests] Deploy synchronously to avoid 
deployment-callback race
cbd7792ee9b is described below

commit cbd7792ee9b3c0e44124634d80f8aa1b55ba9734
Author: Martijn Visser <[email protected]>
AuthorDate: Tue Jun 30 10:38:19 2026 +0200

    [FLINK-38536][tests] Deploy synchronously to avoid deployment-callback race
    
    The FLINK-38536 rewrite (#28350) ran ExecutionGraphFinishTest and 
FinalizeOnMasterTest on a dedicated single-thread main executor while keeping a 
real future executor. Execution.deploy() offloads TaskDeploymentDescriptor 
creation to that future executor and syncs the deploy-completion callback back 
to the main thread asynchronously; tryGetTaskDeploymentDescriptorForSlot fails 
the deployment when the execution is no longer DEPLOYING. The tests moved 
executions to RUNNING via switchAllV [...]
    
    Run scheduling and deployment synchronously on the test thread via 
forMainThread() and DirectScheduledExecutorService so the deployment callbacks 
complete inline while the executions are still DEPLOYING. This supersedes the 
forSingleThreadExecutor approach from #28350 and removes the 
runInMainThread/supplyInMainThread helpers it introduced.
    
    Generated-by: Claude Opus 4.8 (1M context)
---
 .../executiongraph/ExecutionGraphFinishTest.java   | 113 +++++----------------
 .../executiongraph/FinalizeOnMasterTest.java       |  92 ++++-------------
 2 files changed, 48 insertions(+), 157 deletions(-)

diff --git 
a/flink-runtime/src/test/java/org/apache/flink/runtime/executiongraph/ExecutionGraphFinishTest.java
 
b/flink-runtime/src/test/java/org/apache/flink/runtime/executiongraph/ExecutionGraphFinishTest.java
index 148ee0d101e..1f8b40ee9ee 100644
--- 
a/flink-runtime/src/test/java/org/apache/flink/runtime/executiongraph/ExecutionGraphFinishTest.java
+++ 
b/flink-runtime/src/test/java/org/apache/flink/runtime/executiongraph/ExecutionGraphFinishTest.java
@@ -19,43 +19,25 @@
 package org.apache.flink.runtime.executiongraph;
 
 import org.apache.flink.api.common.JobStatus;
-import org.apache.flink.runtime.concurrent.ComponentMainThreadExecutor;
 import 
org.apache.flink.runtime.concurrent.ComponentMainThreadExecutorServiceAdapter;
 import org.apache.flink.runtime.jobgraph.JobGraph;
 import org.apache.flink.runtime.jobgraph.JobGraphTestUtils;
 import org.apache.flink.runtime.scheduler.DefaultSchedulerBuilder;
 import org.apache.flink.runtime.scheduler.SchedulerBase;
 import org.apache.flink.runtime.testtasks.NoOpInvokable;
-import org.apache.flink.testutils.TestingUtils;
-import org.apache.flink.testutils.executor.TestExecutorExtension;
+import org.apache.flink.runtime.testutils.DirectScheduledExecutorService;
 
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 
 import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.function.Supplier;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
 /** Tests the finish behaviour of the {@link ExecutionGraph}. */
 class ExecutionGraphFinishTest {
 
-    /**
-     * Dedicated single-threaded main-thread executor; using forMainThread() 
here would run the
-     * deployment callbacks on the I/O thread and race the test (FLINK-38536).
-     */
-    @RegisterExtension
-    static final TestExecutorExtension<ScheduledExecutorService> 
JM_MAIN_THREAD_EXECUTOR_RESOURCE =
-            TestingUtils.jmMainThreadExecutorExtension();
-
-    @RegisterExtension
-    static final TestExecutorExtension<ScheduledExecutorService> 
EXECUTOR_RESOURCE =
-            TestingUtils.defaultExecutorExtension();
-
     @Test
     void testJobFinishes() throws Exception {
         JobGraph jobGraph =
@@ -63,82 +45,41 @@ class ExecutionGraphFinishTest {
                         ExecutionGraphTestUtils.createJobVertex("Task1", 2, 
NoOpInvokable.class),
                         ExecutionGraphTestUtils.createJobVertex("Task2", 2, 
NoOpInvokable.class));
 
-        final ComponentMainThreadExecutor mainThreadExecutor =
-                
ComponentMainThreadExecutorServiceAdapter.forSingleThreadExecutor(
-                        JM_MAIN_THREAD_EXECUTOR_RESOURCE.getExecutor());
-
+        // Use a direct future executor so the asynchronous deployment 
callbacks complete inline
+        // before the manual state transitions below, avoiding the FLINK-38536 
race.
         SchedulerBase scheduler =
                 new DefaultSchedulerBuilder(
-                                jobGraph, mainThreadExecutor, 
EXECUTOR_RESOURCE.getExecutor())
+                                jobGraph,
+                                
ComponentMainThreadExecutorServiceAdapter.forMainThread(),
+                                new DirectScheduledExecutorService())
                         .build();
 
         ExecutionGraph eg = scheduler.getExecutionGraph();
 
-        runInMainThread(mainThreadExecutor, scheduler::startScheduling);
-        runInMainThread(
-                mainThreadExecutor, () -> 
ExecutionGraphTestUtils.switchAllVerticesToRunning(eg));
-
-        final ExecutionJobVertex[] orderedVertices =
-                supplyInMainThread(
-                        mainThreadExecutor,
-                        () -> {
-                            Iterator<ExecutionJobVertex> jobVertices =
-                                    eg.getVerticesTopologically().iterator();
-                            return new ExecutionJobVertex[] {
-                                jobVertices.next(), jobVertices.next()
-                            };
-                        });
-        ExecutionJobVertex sender = orderedVertices[0];
-        ExecutionJobVertex receiver = orderedVertices[1];
-
-        List<ExecutionVertex> senderVertices =
-                supplyInMainThread(
-                        mainThreadExecutor, () -> 
Arrays.asList(sender.getTaskVertices()));
-        List<ExecutionVertex> receiverVertices =
-                supplyInMainThread(
-                        mainThreadExecutor, () -> 
Arrays.asList(receiver.getTaskVertices()));
+        scheduler.startScheduling();
+        ExecutionGraphTestUtils.switchAllVerticesToRunning(eg);
 
-        // test getNumExecutionVertexFinished
-        runInMainThread(
-                mainThreadExecutor,
-                () -> 
senderVertices.get(0).getCurrentExecutionAttempt().markFinished());
-        assertThat(supplyInMainThread(mainThreadExecutor, 
sender::getNumExecutionVertexFinished))
-                .isOne();
-        assertThat(supplyInMainThread(mainThreadExecutor, eg::getState))
-                .isEqualTo(JobStatus.RUNNING);
-
-        runInMainThread(
-                mainThreadExecutor,
-                () -> 
senderVertices.get(1).getCurrentExecutionAttempt().markFinished());
-        assertThat(supplyInMainThread(mainThreadExecutor, 
sender::getNumExecutionVertexFinished))
-                .isEqualTo(2);
-        assertThat(supplyInMainThread(mainThreadExecutor, eg::getState))
-                .isEqualTo(JobStatus.RUNNING);
+        Iterator<ExecutionJobVertex> jobVertices = 
eg.getVerticesTopologically().iterator();
 
-        // test job finishes
-        runInMainThread(
-                mainThreadExecutor,
-                () -> {
-                    
receiverVertices.get(0).getCurrentExecutionAttempt().markFinished();
-                    
receiverVertices.get(1).getCurrentExecutionAttempt().markFinished();
-                });
-        assertThat(eg.waitUntilTerminal()).isEqualTo(JobStatus.FINISHED);
-        assertThat(supplyInMainThread(mainThreadExecutor, 
eg::getNumFinishedVertices)).isEqualTo(4);
-        assertThat(supplyInMainThread(mainThreadExecutor, eg::getState))
-                .isEqualTo(JobStatus.FINISHED);
-    }
+        ExecutionJobVertex sender = jobVertices.next();
+        ExecutionJobVertex receiver = jobVertices.next();
 
-    /** Runs the action on the JobManager main thread and blocks until it 
completes. */
-    private static void runInMainThread(
-            final ComponentMainThreadExecutor mainThreadExecutor, final 
Runnable action)
-            throws Exception {
-        CompletableFuture.runAsync(action, mainThreadExecutor).get();
-    }
+        List<ExecutionVertex> senderVertices = 
Arrays.asList(sender.getTaskVertices());
+        List<ExecutionVertex> receiverVertices = 
Arrays.asList(receiver.getTaskVertices());
+
+        // test getNumExecutionVertexFinished
+        senderVertices.get(0).getCurrentExecutionAttempt().markFinished();
+        assertThat(sender.getNumExecutionVertexFinished()).isOne();
+        assertThat(eg.getState()).isEqualTo(JobStatus.RUNNING);
 
-    /** Reads the value on the JobManager main thread and blocks until it is 
available. */
-    private static <T> T supplyInMainThread(
-            final ComponentMainThreadExecutor mainThreadExecutor, final 
Supplier<T> supplier)
-            throws Exception {
-        return CompletableFuture.supplyAsync(supplier, 
mainThreadExecutor).get();
+        senderVertices.get(1).getCurrentExecutionAttempt().markFinished();
+        assertThat(sender.getNumExecutionVertexFinished()).isEqualTo(2);
+        assertThat(eg.getState()).isEqualTo(JobStatus.RUNNING);
+
+        // test job finishes
+        receiverVertices.get(0).getCurrentExecutionAttempt().markFinished();
+        receiverVertices.get(1).getCurrentExecutionAttempt().markFinished();
+        assertThat(eg.getNumFinishedVertices()).isEqualTo(4);
+        assertThat(eg.getState()).isEqualTo(JobStatus.FINISHED);
     }
 }
diff --git 
a/flink-runtime/src/test/java/org/apache/flink/runtime/executiongraph/FinalizeOnMasterTest.java
 
b/flink-runtime/src/test/java/org/apache/flink/runtime/executiongraph/FinalizeOnMasterTest.java
index a2b792ee239..9e510ddb660 100644
--- 
a/flink-runtime/src/test/java/org/apache/flink/runtime/executiongraph/FinalizeOnMasterTest.java
+++ 
b/flink-runtime/src/test/java/org/apache/flink/runtime/executiongraph/FinalizeOnMasterTest.java
@@ -19,22 +19,15 @@
 package org.apache.flink.runtime.executiongraph;
 
 import org.apache.flink.api.common.JobStatus;
-import org.apache.flink.runtime.concurrent.ComponentMainThreadExecutor;
 import 
org.apache.flink.runtime.concurrent.ComponentMainThreadExecutorServiceAdapter;
 import org.apache.flink.runtime.jobgraph.JobGraphTestUtils;
 import org.apache.flink.runtime.jobgraph.JobVertex;
 import org.apache.flink.runtime.jobgraph.JobVertex.FinalizeOnMasterContext;
 import org.apache.flink.runtime.scheduler.SchedulerBase;
 import org.apache.flink.runtime.testtasks.NoOpInvokable;
-import org.apache.flink.testutils.TestingUtils;
-import org.apache.flink.testutils.executor.TestExecutorExtension;
+import org.apache.flink.runtime.testutils.DirectScheduledExecutorService;
 
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
-
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.function.Supplier;
 
 import static 
org.apache.flink.runtime.scheduler.SchedulerTestingUtils.createScheduler;
 import static org.assertj.core.api.Assertions.assertThat;
@@ -49,18 +42,6 @@ import static org.mockito.Mockito.verify;
  */
 class FinalizeOnMasterTest {
 
-    /**
-     * Dedicated single-threaded main-thread executor; using forMainThread() 
here would run the
-     * deployment callbacks on the I/O thread and race the test (FLINK-38536).
-     */
-    @RegisterExtension
-    static final TestExecutorExtension<ScheduledExecutorService> 
JM_MAIN_THREAD_EXECUTOR_RESOURCE =
-            TestingUtils.jmMainThreadExecutorExtension();
-
-    @RegisterExtension
-    static final TestExecutorExtension<ScheduledExecutorService> 
EXECUTOR_RESOURCE =
-            TestingUtils.defaultExecutorExtension();
-
     @Test
     void testFinalizeIsCalledUponSuccess() throws Exception {
         final JobVertex vertex1 = spy(new JobVertex("test vertex 1"));
@@ -71,35 +52,28 @@ class FinalizeOnMasterTest {
         vertex2.setInvokableClass(NoOpInvokable.class);
         vertex2.setParallelism(2);
 
-        final ComponentMainThreadExecutor mainThreadExecutor =
-                
ComponentMainThreadExecutorServiceAdapter.forSingleThreadExecutor(
-                        JM_MAIN_THREAD_EXECUTOR_RESOURCE.getExecutor());
-
+        // Use a direct future executor so the asynchronous deployment 
callbacks complete inline
+        // before the manual state transitions below, avoiding the FLINK-38536 
race.
         final SchedulerBase scheduler =
                 createScheduler(
                         JobGraphTestUtils.streamingJobGraph(vertex1, vertex2),
-                        mainThreadExecutor,
-                        EXECUTOR_RESOURCE.getExecutor());
+                        
ComponentMainThreadExecutorServiceAdapter.forMainThread(),
+                        new DirectScheduledExecutorService());
+        scheduler.startScheduling();
 
         final ExecutionGraph eg = scheduler.getExecutionGraph();
+        assertThat(eg.getState()).isEqualTo(JobStatus.RUNNING);
 
-        runInMainThread(mainThreadExecutor, scheduler::startScheduling);
-        assertThat(supplyInMainThread(mainThreadExecutor, eg::getState))
-                .isEqualTo(JobStatus.RUNNING);
-
-        runInMainThread(
-                mainThreadExecutor, () -> 
ExecutionGraphTestUtils.switchAllVerticesToRunning(eg));
+        ExecutionGraphTestUtils.switchAllVerticesToRunning(eg);
 
         // move all vertices to finished state
-        runInMainThread(mainThreadExecutor, () -> 
ExecutionGraphTestUtils.finishAllVertices(eg));
+        ExecutionGraphTestUtils.finishAllVertices(eg);
         assertThat(eg.waitUntilTerminal()).isEqualTo(JobStatus.FINISHED);
 
-        // waitUntilTerminal acts as the barrier, so the assertions below are 
safe off the main
-        // thread.
         verify(vertex1, 
times(1)).finalizeOnMaster(any(FinalizeOnMasterContext.class));
         verify(vertex2, 
times(1)).finalizeOnMaster(any(FinalizeOnMasterContext.class));
 
-        assertThat(supplyInMainThread(mainThreadExecutor, 
eg::getRegisteredExecutions)).isEmpty();
+        assertThat(eg.getRegisteredExecutions()).isEmpty();
     }
 
     @Test
@@ -108,54 +82,30 @@ class FinalizeOnMasterTest {
         vertex.setInvokableClass(NoOpInvokable.class);
         vertex.setParallelism(1);
 
-        final ComponentMainThreadExecutor mainThreadExecutor =
-                
ComponentMainThreadExecutorServiceAdapter.forSingleThreadExecutor(
-                        JM_MAIN_THREAD_EXECUTOR_RESOURCE.getExecutor());
-
+        // Use a direct future executor so the asynchronous deployment 
callbacks complete inline
+        // before the manual state transitions below, avoiding the FLINK-38536 
race.
         final SchedulerBase scheduler =
                 createScheduler(
                         JobGraphTestUtils.streamingJobGraph(vertex),
-                        mainThreadExecutor,
-                        EXECUTOR_RESOURCE.getExecutor());
+                        
ComponentMainThreadExecutorServiceAdapter.forMainThread(),
+                        new DirectScheduledExecutorService());
+        scheduler.startScheduling();
 
         final ExecutionGraph eg = scheduler.getExecutionGraph();
 
-        runInMainThread(mainThreadExecutor, scheduler::startScheduling);
-        assertThat(supplyInMainThread(mainThreadExecutor, eg::getState))
-                .isEqualTo(JobStatus.RUNNING);
+        assertThat(eg.getState()).isEqualTo(JobStatus.RUNNING);
 
-        runInMainThread(
-                mainThreadExecutor, () -> 
ExecutionGraphTestUtils.switchAllVerticesToRunning(eg));
+        ExecutionGraphTestUtils.switchAllVerticesToRunning(eg);
 
         // fail the execution
-        runInMainThread(
-                mainThreadExecutor,
-                () -> {
-                    final Execution exec =
-                            eg.getJobVertex(vertex.getID())
-                                    .getTaskVertices()[0]
-                                    .getCurrentExecutionAttempt();
-                    exec.fail(new Exception("test"));
-                });
+        final Execution exec =
+                
eg.getJobVertex(vertex.getID()).getTaskVertices()[0].getCurrentExecutionAttempt();
+        exec.fail(new Exception("test"));
 
         assertThat(eg.waitUntilTerminal()).isEqualTo(JobStatus.FAILED);
 
         verify(vertex, 
times(0)).finalizeOnMaster(any(FinalizeOnMasterContext.class));
 
-        assertThat(supplyInMainThread(mainThreadExecutor, 
eg::getRegisteredExecutions)).isEmpty();
-    }
-
-    /** Runs the action on the JobManager main thread and blocks until it 
completes. */
-    private static void runInMainThread(
-            final ComponentMainThreadExecutor mainThreadExecutor, final 
Runnable action)
-            throws Exception {
-        CompletableFuture.runAsync(action, mainThreadExecutor).get();
-    }
-
-    /** Reads the value on the JobManager main thread and blocks until it is 
available. */
-    private static <T> T supplyInMainThread(
-            final ComponentMainThreadExecutor mainThreadExecutor, final 
Supplier<T> supplier)
-            throws Exception {
-        return CompletableFuture.supplyAsync(supplier, 
mainThreadExecutor).get();
+        assertThat(eg.getRegisteredExecutions()).isEmpty();
     }
 }

Reply via email to