XComp commented on code in PR #22341:
URL: https://github.com/apache/flink/pull/22341#discussion_r1263411729


##########
flink-runtime/src/main/java/org/apache/flink/runtime/highavailability/JobResultStore.java:
##########
@@ -43,69 +43,67 @@ public interface JobResultStore {
      * Registers the passed {@link JobResultEntry} instance as {@code dirty} 
which indicates that
      * clean-up operations still need to be performed. Once the job resource 
cleanup has been
      * finalized, we can mark the {@code JobResultEntry} as {@code clean} 
result using {@link
-     * #markResultAsClean(JobID)}.
+     * #markResultAsCleanAsync(JobID)}.
      *
      * @param jobResultEntry The job result we wish to persist.
-     * @throws IOException if the creation of the dirty result failed for IO 
reasons.
-     * @throws IllegalStateException if the passed {@code jobResultEntry} has 
a {@code JobID}
-     *     attached that is already registered in this {@code JobResultStore}.
+     * @return CompletableFuture it the future with {@code true} if the dirty 
result is created
+     *     successfully, otherwise will throw {@link IllegalStateException} if 
the passed {@code
+     *     jobResultEntry} has a {@code JobID} attached that is already 
registered in this {@code
+     *     JobResultStore}.
      */
-    void createDirtyResult(JobResultEntry jobResultEntry) throws IOException, 
IllegalStateException;
+    CompletableFuture<Boolean> createDirtyResultAsync(JobResultEntry 
jobResultEntry);

Review Comment:
   Why did we change from `CompletableFuture<Void>` to 
`CompletableFuture<Boolean>`? Boolean doesn't add any value here. Or am I 
missing something :thinking: 



##########
flink-runtime/src/main/java/org/apache/flink/runtime/highavailability/JobResultStore.java:
##########
@@ -43,69 +43,67 @@ public interface JobResultStore {
      * Registers the passed {@link JobResultEntry} instance as {@code dirty} 
which indicates that
      * clean-up operations still need to be performed. Once the job resource 
cleanup has been
      * finalized, we can mark the {@code JobResultEntry} as {@code clean} 
result using {@link
-     * #markResultAsClean(JobID)}.
+     * #markResultAsCleanAsync(JobID)}.
      *
      * @param jobResultEntry The job result we wish to persist.
-     * @throws IOException if the creation of the dirty result failed for IO 
reasons.
-     * @throws IllegalStateException if the passed {@code jobResultEntry} has 
a {@code JobID}
-     *     attached that is already registered in this {@code JobResultStore}.
+     * @return CompletableFuture it the future with {@code true} if the dirty 
result is created

Review Comment:
   ```suggestion
        * @return a successfully completed future with {@code true} if the 
dirty result is created
   ```
   nit



##########
flink-runtime/src/main/java/org/apache/flink/runtime/highavailability/nonha/embedded/EmbeddedJobResultStore.java:
##########


Review Comment:
   `markResultAsCleanInternal` has an obsolete `IOException` declaration in the 
method signature



##########
flink-runtime/src/main/java/org/apache/flink/runtime/highavailability/AbstractThreadsafeJobResultStore.java:
##########
@@ -44,64 +45,87 @@ public abstract class AbstractThreadsafeJobResultStore 
implements JobResultStore
     private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
 
     @Override
-    public void createDirtyResult(JobResultEntry jobResultEntry) throws 
IOException {
-        Preconditions.checkState(
-                !hasJobResultEntry(jobResultEntry.getJobId()),
-                "Job result store already contains an entry for job %s",
-                jobResultEntry.getJobId());
-
-        withWriteLock(() -> createDirtyResultInternal(jobResultEntry));
+    public CompletableFuture<Boolean> createDirtyResultAsync(JobResultEntry 
jobResultEntry) {
+        return hasJobResultEntryAsync(jobResultEntry.getJobId())
+                .handle(
+                        (hasResult, error) -> {
+                            if (error != null || hasResult) {

Review Comment:
   There's a `ExceptionUtils.tryRethrowException` method that's used to handle 
the case of `error` not being null. `hasResult` being false can be kept in the 
Precondition that's also used in the original code to reduce the diff. WDYT?



##########
flink-runtime/src/test/java/org/apache/flink/runtime/dispatcher/DispatcherResourceCleanupTest.java:
##########
@@ -332,11 +332,16 @@ public void testJobBeingMarkedAsDirtyBeforeCleanup() 
throws Exception {
                                 TestingJobResultStore.builder()
                                         .withCreateDirtyResultConsumer(
                                                 ignoredJobResultEntry -> {
+                                                    CompletableFuture<Boolean> 
result =
+                                                            new 
CompletableFuture<>();
                                                     try {
                                                         
markAsDirtyLatch.await();
                                                     } catch 
(InterruptedException e) {
-                                                        throw new 
RuntimeException(e);
+                                                        
result.completeExceptionally(
+                                                                new 
RuntimeException(e));

Review Comment:
   We shouldn't swallow the `InteruptedException`. Instead, we should call 
`Thread.currentThread().interrupt()` to continue the initiated interruption.



##########
flink-runtime/src/test/java/org/apache/flink/runtime/dispatcher/DispatcherResourceCleanupTest.java:
##########
@@ -332,11 +332,16 @@ public void testJobBeingMarkedAsDirtyBeforeCleanup() 
throws Exception {
                                 TestingJobResultStore.builder()
                                         .withCreateDirtyResultConsumer(
                                                 ignoredJobResultEntry -> {
+                                                    CompletableFuture<Boolean> 
result =
+                                                            new 
CompletableFuture<>();
                                                     try {
                                                         
markAsDirtyLatch.await();
                                                     } catch 
(InterruptedException e) {
-                                                        throw new 
RuntimeException(e);
+                                                        
result.completeExceptionally(
+                                                                new 
RuntimeException(e));
                                                     }
+                                                    result.complete(true);
+                                                    return result;

Review Comment:
   ```suggestion
                                                       return 
CompletableFuture.completedFuture(true);
   ```
   Even though, I'm still not sure whether we need a `boolean` return value (in 
that case `FutureUtils.completedVoidFuture()` would be a valid factory method 
for creating the return value.



##########
flink-runtime/src/test/java/org/apache/flink/runtime/dispatcher/DispatcherResourceCleanupTest.java:
##########
@@ -548,7 +557,10 @@ public void 
testFatalErrorIfJobCannotBeMarkedDirtyInJobResultStore() throws Exce
                 TestingJobResultStore.builder()
                         .withCreateDirtyResultConsumer(
                                 jobResult -> {
-                                    throw new IOException("Expected 
IOException.");
+                                    CompletableFuture<Boolean> future = new 
CompletableFuture<>();
+                                    future.completeExceptionally(
+                                            new IOException("Expected 
IOException."));
+                                    return future;

Review Comment:
   ```suggestion
                                       return 
FutureUtils.completedExceptionally(new IOException("Expected IOException."))
   ```



##########
flink-runtime/src/main/java/org/apache/flink/runtime/dispatcher/Dispatcher.java:
##########
@@ -1262,14 +1277,8 @@ private CompletableFuture<Void> removeJob(JobID jobId, 
CleanupJobState cleanupJo
         }
     }
 
-    private void markJobAsClean(JobID jobId) {
-        try {
-            jobResultStore.markResultAsClean(jobId);
-            log.debug(
-                    "Cleanup for the job '{}' has finished. Job has been 
marked as clean.", jobId);
-        } catch (IOException e) {
-            log.warn("Could not properly mark job {} result as clean.", jobId, 
e);
-        }
+    private CompletableFuture<Void> markJobAsCleanAsync(JobID jobId) {

Review Comment:
   You would rather have a `thenRun` in this method to trigger the debug log 
message.



##########
flink-runtime/src/main/java/org/apache/flink/runtime/dispatcher/Dispatcher.java:
##########
@@ -575,12 +576,13 @@ private boolean isDuplicateJob(JobID jobId) throws 
FlinkException {
      */
     private boolean isInGloballyTerminalState(JobID jobId) throws 
FlinkException {
         try {
-            return jobResultStore.hasJobResultEntry(jobId);
-        } catch (IOException e) {
-            throw new FlinkException(
-                    String.format("Failed to retrieve job scheduling status 
for job %s.", jobId),
-                    e);
+            return jobResultStore.hasJobResultEntryAsync(jobId).get();

Review Comment:
   This hasn't been addressed.



##########
flink-runtime/src/main/java/org/apache/flink/runtime/dispatcher/runner/SessionDispatcherLeaderProcess.java:
##########
@@ -191,8 +191,8 @@ private Collection<JobResult> getDirtyJobResultsIfRunning() 
{
 
     private Collection<JobResult> getDirtyJobResults() {
         try {
-            return jobResultStore.getDirtyResults();
-        } catch (IOException e) {
+            return jobResultStore.getDirtyResultsAsync().get();

Review Comment:
   I added a related comment in `JobDispatcherLeaderProcessFactoryFactory`.



##########
flink-runtime/src/main/java/org/apache/flink/runtime/highavailability/JobResultStore.java:
##########
@@ -43,69 +43,67 @@ public interface JobResultStore {
      * Registers the passed {@link JobResultEntry} instance as {@code dirty} 
which indicates that
      * clean-up operations still need to be performed. Once the job resource 
cleanup has been
      * finalized, we can mark the {@code JobResultEntry} as {@code clean} 
result using {@link
-     * #markResultAsClean(JobID)}.
+     * #markResultAsCleanAsync(JobID)}.
      *
      * @param jobResultEntry The job result we wish to persist.
-     * @throws IOException if the creation of the dirty result failed for IO 
reasons.
-     * @throws IllegalStateException if the passed {@code jobResultEntry} has 
a {@code JobID}
-     *     attached that is already registered in this {@code JobResultStore}.
+     * @return CompletableFuture it the future with {@code true} if the dirty 
result is created
+     *     successfully, otherwise will throw {@link IllegalStateException} if 
the passed {@code
+     *     jobResultEntry} has a {@code JobID} attached that is already 
registered in this {@code
+     *     JobResultStore}.
      */
-    void createDirtyResult(JobResultEntry jobResultEntry) throws IOException, 
IllegalStateException;
+    CompletableFuture<Boolean> createDirtyResultAsync(JobResultEntry 
jobResultEntry);
 
     /**
      * Marks an existing {@link JobResultEntry} as {@code clean}. This 
indicates that no more
      * resource cleanup steps need to be performed. No actions should be 
triggered if the passed
      * {@code JobID} belongs to a job that was already marked as clean.
      *
      * @param jobId Ident of the job we wish to mark as clean.
-     * @throws IOException if marking the {@code dirty} {@code JobResultEntry} 
as {@code clean}
-     *     failed for IO reasons.
-     * @throws NoSuchElementException if there is no corresponding {@code 
dirty} job present in the
+     * @return CompletableFuture is the future with the completed state, which 
will throw {@link

Review Comment:
   ```suggestion
        * @return the future with the completed state, which will complete 
exceptionally with {@link
   ```
   That's a nitty one as well, but: The future doesn't throw (implying that 
it's an actor) but rather fails (being a passive component). The future's 
`get()` call would through the exception.



##########
flink-runtime/src/main/java/org/apache/flink/runtime/highavailability/FileSystemJobResultStore.java:
##########
@@ -181,37 +188,70 @@ public void markResultAsCleanInternal(JobID jobId) throws 
IOException, NoSuchEle
     }
 
     @Override
-    public boolean hasDirtyJobResultEntryInternal(JobID jobId) throws 
IOException {
-        return fileSystem.exists(constructDirtyPath(jobId));
+    public CompletableFuture<Boolean> hasDirtyJobResultEntryInternal(JobID 
jobId) {
+        CompletableFuture<Boolean> hasDirtyJobResultEntryFuture = new 
CompletableFuture<>();
+        ioExecutor.execute(
+                () -> {
+                    try {
+                        hasDirtyJobResultEntryFuture.complete(
+                                fileSystem.exists(constructDirtyPath(jobId)));
+                    } catch (IOException e) {
+                        hasDirtyJobResultEntryFuture.completeExceptionally(e);
+                    }
+                });
+        return hasDirtyJobResultEntryFuture;

Review Comment:
   ```suggestion
   return FutureUtils.supplyAsync(
                   () -> fileSystem.exists(constructDirtyPath(jobId)), 
ioExecutor);        
   ```
   There's a shorter version for these kind of code constructrs.



##########
flink-runtime/src/main/java/org/apache/flink/runtime/dispatcher/runner/JobDispatcherLeaderProcessFactoryFactory.java:
##########
@@ -101,14 +109,9 @@ public static JobDispatcherLeaderProcessFactoryFactory 
create(
         return new JobDispatcherLeaderProcessFactoryFactory(jobGraphRetriever);
     }
 
-    private static Collection<JobResult> getDirtyJobResults(JobResultStore 
jobResultStore) {
-        try {
-            return jobResultStore.getDirtyResults();
-        } catch (IOException e) {
-            throw new FlinkRuntimeException(
-                    "Could not retrieve the JobResults of dirty jobs from the 
underlying JobResultStore.",
-                    e);
-        }
+    private static CompletableFuture<Set<JobResult>> getDirtyJobResultsAsync(

Review Comment:
   This method doesn't really have a usecase anymore. We're just calling 
`jobResultStore.getDirtyResultsAsync()` without any extra logic.



##########
flink-runtime/src/test/java/org/apache/flink/runtime/dispatcher/DispatcherResourceCleanupTest.java:
##########
@@ -332,11 +332,16 @@ public void testJobBeingMarkedAsDirtyBeforeCleanup() 
throws Exception {
                                 TestingJobResultStore.builder()
                                         .withCreateDirtyResultConsumer(
                                                 ignoredJobResultEntry -> {
+                                                    CompletableFuture<Boolean> 
result =
+                                                            new 
CompletableFuture<>();
                                                     try {
                                                         
markAsDirtyLatch.await();
                                                     } catch 
(InterruptedException e) {
-                                                        throw new 
RuntimeException(e);
+                                                        
result.completeExceptionally(
+                                                                new 
RuntimeException(e));

Review Comment:
   What's the reason for adding the `RuntimeException` as a wrapper here?



##########
flink-runtime/src/main/java/org/apache/flink/runtime/dispatcher/runner/JobDispatcherLeaderProcessFactoryFactory.java:
##########
@@ -77,7 +78,14 @@ public JobDispatcherLeaderProcessFactory createFactory(
         }
 
         final JobResultStore jobResultStore = 
jobPersistenceComponentFactory.createJobResultStore();
-        final Collection<JobResult> recoveredDirtyJobResults = 
getDirtyJobResults(jobResultStore);
+        Collection<JobResult> recoveredDirtyJobResults;

Review Comment:
   ```suggestion
           final Collection<JobResult> recoveredDirtyJobResults;
   ```
   The `final` keyword can stay



##########
flink-runtime/src/main/java/org/apache/flink/runtime/highavailability/FileSystemJobResultStore.java:
##########
@@ -181,37 +188,70 @@ public void markResultAsCleanInternal(JobID jobId) throws 
IOException, NoSuchEle
     }
 
     @Override
-    public boolean hasDirtyJobResultEntryInternal(JobID jobId) throws 
IOException {
-        return fileSystem.exists(constructDirtyPath(jobId));
+    public CompletableFuture<Boolean> hasDirtyJobResultEntryInternal(JobID 
jobId) {
+        CompletableFuture<Boolean> hasDirtyJobResultEntryFuture = new 
CompletableFuture<>();
+        ioExecutor.execute(
+                () -> {
+                    try {
+                        hasDirtyJobResultEntryFuture.complete(
+                                fileSystem.exists(constructDirtyPath(jobId)));
+                    } catch (IOException e) {
+                        hasDirtyJobResultEntryFuture.completeExceptionally(e);
+                    }
+                });
+        return hasDirtyJobResultEntryFuture;
     }
 
     @Override
-    public boolean hasCleanJobResultEntryInternal(JobID jobId) throws 
IOException {
-        return fileSystem.exists(constructCleanPath(jobId));
+    public CompletableFuture<Boolean> hasCleanJobResultEntryInternal(JobID 
jobId) {
+        CompletableFuture<Boolean> hasCleanJobResultEntryFuture = new 
CompletableFuture<>();

Review Comment:
   Here you can use `FutureUtils.supplyAsync` as well



##########
flink-runtime/src/test/java/org/apache/flink/runtime/dispatcher/DispatcherResourceCleanupTest.java:
##########
@@ -579,10 +591,17 @@ public void 
testErrorHandlingIfJobCannotBeMarkedAsCleanInJobResultStore() throws
         final CompletableFuture<JobResultEntry> dirtyJobFuture = new 
CompletableFuture<>();
         final JobResultStore jobResultStore =
                 TestingJobResultStore.builder()
-                        
.withCreateDirtyResultConsumer(dirtyJobFuture::complete)
+                        .withCreateDirtyResultConsumer(
+                                jobResultEntry -> {
+                                    dirtyJobFuture.complete(jobResultEntry);
+                                    return 
CompletableFuture.completedFuture(true);
+                                })
                         .withMarkResultAsCleanConsumer(
                                 jobId -> {
-                                    throw new IOException("Expected 
IOException.");
+                                    CompletableFuture<Void> result = new 
CompletableFuture<>();
+                                    result.completeExceptionally(
+                                            new IOException("Expected 
IOException."));
+                                    return result;

Review Comment:
   ```suggestion
                                       return 
FutureUtils.completedExceptionally(new IOException("Expected IOException."));
   ```



##########
flink-runtime/src/test/java/org/apache/flink/runtime/dispatcher/DispatcherResourceCleanupTest.java:
##########
@@ -358,7 +363,11 @@ public void testJobBeingMarkedAsCleanAfterCleanup() throws 
Exception {
 
         final JobResultStore jobResultStore =
                 TestingJobResultStore.builder()
-                        
.withMarkResultAsCleanConsumer(markAsCleanFuture::complete)
+                        .withMarkResultAsCleanConsumer(
+                                jobID -> {
+                                    markAsCleanFuture.complete(jobID);
+                                    return 
CompletableFuture.completedFuture(null);

Review Comment:
   nit: `FutureUtils.completedVoidFuture()` doesn't create a new instance. That 
doesn't make, admittedly, a difference in this test implementation. I wanted to 
mention it, anyway.



##########
flink-runtime/src/main/java/org/apache/flink/runtime/highavailability/JobResultStore.java:
##########
@@ -43,69 +43,67 @@ public interface JobResultStore {
      * Registers the passed {@link JobResultEntry} instance as {@code dirty} 
which indicates that
      * clean-up operations still need to be performed. Once the job resource 
cleanup has been
      * finalized, we can mark the {@code JobResultEntry} as {@code clean} 
result using {@link
-     * #markResultAsClean(JobID)}.
+     * #markResultAsCleanAsync(JobID)}.
      *
      * @param jobResultEntry The job result we wish to persist.
-     * @throws IOException if the creation of the dirty result failed for IO 
reasons.
-     * @throws IllegalStateException if the passed {@code jobResultEntry} has 
a {@code JobID}
-     *     attached that is already registered in this {@code JobResultStore}.
+     * @return CompletableFuture it the future with {@code true} if the dirty 
result is created
+     *     successfully, otherwise will throw {@link IllegalStateException} if 
the passed {@code
+     *     jobResultEntry} has a {@code JobID} attached that is already 
registered in this {@code
+     *     JobResultStore}.
      */
-    void createDirtyResult(JobResultEntry jobResultEntry) throws IOException, 
IllegalStateException;
+    CompletableFuture<Boolean> createDirtyResultAsync(JobResultEntry 
jobResultEntry);
 
     /**
      * Marks an existing {@link JobResultEntry} as {@code clean}. This 
indicates that no more
      * resource cleanup steps need to be performed. No actions should be 
triggered if the passed
      * {@code JobID} belongs to a job that was already marked as clean.
      *
      * @param jobId Ident of the job we wish to mark as clean.
-     * @throws IOException if marking the {@code dirty} {@code JobResultEntry} 
as {@code clean}
-     *     failed for IO reasons.
-     * @throws NoSuchElementException if there is no corresponding {@code 
dirty} job present in the
+     * @return CompletableFuture is the future with the completed state, which 
will throw {@link
+     *     NoSuchElementException} if there is no corresponding {@code dirty} 
job present in the
      *     store for the given {@code JobID}.
      */
-    void markResultAsClean(JobID jobId) throws IOException, 
NoSuchElementException;
+    CompletableFuture<Void> markResultAsCleanAsync(JobID jobId);
 
     /**
-     * Returns whether the store already contains an entry for a job.
+     * Returns the future of whether the store already contains an entry for a 
job.
      *
      * @param jobId Ident of the job we wish to check the store for.
-     * @return {@code true} if a {@code dirty} or {@code clean} {@link 
JobResultEntry} exists for
-     *     the given {@code JobID}; otherwise {@code false}.
-     * @throws IOException if determining whether a job entry is present in 
the store failed for IO
-     *     reasons.
+     * @return CompletableFuture with {@code true} if a {@code dirty} or 
{@code clean} {@link
+     *     JobResultEntry} exists for the given {@code JobID}; otherwise 
{@code false}.
      */
-    default boolean hasJobResultEntry(JobID jobId) throws IOException {
-        return hasDirtyJobResultEntry(jobId) || hasCleanJobResultEntry(jobId);
+    default CompletableFuture<Boolean> hasJobResultEntryAsync(JobID jobId) {
+        return hasDirtyJobResultEntryAsync(jobId)
+                .thenCombine(
+                        hasCleanJobResultEntryAsync(jobId),
+                        (result1, result2) -> result1 || result2);
     }
 
     /**
-     * Returns whether the store already contains a {@code dirty} entry for 
the given {@code JobID}.
+     * Returns the future of whether the store contains a {@code dirty} entry 
for the given {@code
+     * JobID}.
      *
      * @param jobId Ident of the job we wish to check the store for.
-     * @return {@code true}, if a {@code dirty} entry exists for the given 
{@code JobID}; otherwise
-     *     {@code false}.
-     * @throws IOException if determining whether a job entry is present in 
the store failed for IO
-     *     reasons.
+     * @return CompletableFuture with value of {@code true}, if a {@code 
dirty} entry exists for the
+     *     given {@code JobID}; otherwise Completable with value of {@code 
false}.
      */
-    boolean hasDirtyJobResultEntry(JobID jobId) throws IOException;
+    CompletableFuture<Boolean> hasDirtyJobResultEntryAsync(JobID jobId);
 
     /**
-     * Returns whether the store already contains a {@code clean} entry for 
the given {@code JobID}.
+     * Returns the future of whether the store contains a {@code clean} entry 
for the given {@code
+     * JobID}.
      *
      * @param jobId Ident of the job we wish to check the store for.
-     * @return {@code true}, if a {@code clean} entry exists for the given 
{@code JobID}; otherwise
-     *     {@code false}.
-     * @throws IOException if determining whether a job entry is present in 
the store failed for IO
-     *     reasons.
+     * @return CompletableFuture with value of {@code true}, if a {@code 
clean} entry exists for the
+     *     given {@code JobID}; otherwise Completable with value of {@code 
false}.
      */
-    boolean hasCleanJobResultEntry(JobID jobId) throws IOException;
+    CompletableFuture<Boolean> hasCleanJobResultEntryAsync(JobID jobId);
 
     /**
-     * Get the persisted {@link JobResult} instances that are marked as {@code 
dirty}. This is
-     * useful for recovery of finalization steps.
+     * Returns the future of persisted {@link JobResult} instances that are 
marked as {@code dirty}.
+     * This is useful for recovery of finalization steps.
      *
-     * @return A set of dirty {@code JobResults} from the store.
-     * @throws IOException if collecting the set of dirty results failed for 
IO reasons.
+     * @return CompletableFuture with value of a set of dirty {@code 
JobResults} from the store.
      */
-    Set<JobResult> getDirtyResults() throws IOException;
+    CompletableFuture<Set<JobResult>> getDirtyResultsAsync();

Review Comment:
   Just to repeat what I already said in another comment: Looks like we can 
revert making this method asynchronous.



##########
flink-runtime/src/main/java/org/apache/flink/runtime/dispatcher/Dispatcher.java:
##########
@@ -1251,7 +1253,20 @@ private CompletableFuture<Void> removeJob(JobID jobId, 
CleanupJobState cleanupJo
         if (cleanupJobState.isGlobalCleanup()) {
             return globalResourceCleaner
                     .cleanupAsync(jobId)
-                    .thenRunAsync(() -> markJobAsClean(jobId), ioExecutor)
+                    .thenRunAsync(

Review Comment:
   You could use `thenComposeAsync` here to avoid the `.get()` call. I'm 
wondering why we're using `Async here instead of just `thenRun` :thinking: 



##########
flink-runtime/src/test/java/org/apache/flink/runtime/highavailability/FileSystemJobResultStoreContractTest.java:
##########
@@ -35,6 +35,6 @@ public class FileSystemJobResultStoreContractTest implements 
JobResultStoreContr
     @Override
     public JobResultStore createJobResultStore() throws IOException {
         Path path = new Path(temporaryFolder.toURI());
-        return new FileSystemJobResultStore(path.getFileSystem(), path, false);
+        return new FileSystemJobResultStore(path.getFileSystem(), path, false, 
Runnable::run);

Review Comment:
   nit: `Executors.directExecutor()` would be the usual Executor for direct 
execution.



##########
flink-runtime/src/test/java/org/apache/flink/runtime/highavailability/FileSystemJobResultStoreFileOperationsTest.java:
##########
@@ -54,11 +58,19 @@ public class FileSystemJobResultStoreFileOperationsTest {
 
     private Path basePath;
 
+    private ExecutorService ioExecutor;
+
     @BeforeEach
     public void setupTest() throws IOException {
         basePath = new Path(temporaryFolder.toURI());
+        ioExecutor = Executors.newSingleThreadExecutor();

Review Comment:
   I guess, for the test cases in this class, the `directExecutor` is good 
enough (rather than creating a new thread). You could also use 
`ManuallyTriggeredScheduledExecutor` if you want to verify delay in the 
execution.



##########
flink-runtime/src/main/java/org/apache/flink/runtime/highavailability/AbstractThreadsafeJobResultStore.java:
##########
@@ -44,64 +45,84 @@ public abstract class AbstractThreadsafeJobResultStore 
implements JobResultStore
     private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
 
     @Override
-    public void createDirtyResult(JobResultEntry jobResultEntry) throws 
IOException {
-        Preconditions.checkState(
-                !hasJobResultEntry(jobResultEntry.getJobId()),
-                "Job result store already contains an entry for job %s",
-                jobResultEntry.getJobId());
-
-        withWriteLock(() -> createDirtyResultInternal(jobResultEntry));
+    public CompletableFuture<Void> createDirtyResultAsync(JobResultEntry 
jobResultEntry) {
+        return hasJobResultEntryAsync(jobResultEntry.getJobId())
+                .handle(
+                        (hasResult, error) -> {
+                            if (error != null || hasResult) {
+                                ExceptionUtils.rethrow(error);
+                            }
+                            try {
+                                withWriteLock(() -> 
createDirtyResultInternal(jobResultEntry));

Review Comment:
   Can't we move the asynchronous handling into 
`AbstractThreadsafeJobResultStore`. The `AbstractThreadsafeJobResultStore` 
would need the `executor` to be passed as a parameter in its constructor. The 
`EmbeddedJobResultStore` could then use `Executors.directExecutor()` which 
essentially handles the execution synchronously. WDYT?



##########
flink-runtime/src/main/java/org/apache/flink/runtime/dispatcher/runner/JobDispatcherLeaderProcessFactoryFactory.java:
##########
@@ -103,8 +103,8 @@ public static JobDispatcherLeaderProcessFactoryFactory 
create(
 
     private static Collection<JobResult> getDirtyJobResults(JobResultStore 
jobResultStore) {
         try {
-            return jobResultStore.getDirtyResults();
-        } catch (IOException e) {
+            return jobResultStore.getDirtyResultsAsync().get();

Review Comment:
   Here, I unfortunately misguided you: You just moved the error handling out 
of this method. Instead, the calling code does the error handling 
(`getDirtyJobResults` becomes obsoletes as part of this refactoring because it 
just calls a single method).
   
   The calling code, indeed, has to wait for the dirty results to be returned 
synchronously. I'm wondering whether we should simplify the `JobResultStore` 
interface here and revert the async refactoring for this specific method.
   
   Thinking about it, we could even separate the interfaces here: The 
`getDirtyJobResult` is used in the `DispatcherLeaderProcessFactoryFactory` 
implementations (something like `DirtyJobResultRetriever`). The other (now 
asynchronous) methods are used in the `Dispatcher` where we would want to have 
asynchronous access (`JobResultStoreAsync`). Anyway, I guess, that's not really 
necessary in this PR now. But reverting `getDirtyJobResult` appears to be 
reasonable. WDYT?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to