weiqingy commented on code in PR #987:
URL: https://github.com/apache/flink-agents/pull/987#discussion_r3745804607


##########
runtime/src/test/java/org/apache/flink/agents/runtime/operator/PythonBridgeManagerTest.java:
##########
@@ -59,4 +69,98 @@ void openIsNoOpWhenPlanHasNeitherPythonActionsNorResources() 
throws Exception {
             assertThat(bridge.getPythonRunnerContext()).isNull();
         }
     }
+
+    /**
+     * A failing action executor must not strand the interpreter or the 
environment manager: both
+     * hold native Python state that leaks for the lifetime of the TaskManager 
if never closed.
+     *
+     * <p>Also pins the close order documented on the class, which is 
load-bearing rather than
+     * incidental: {@link PythonActionExecutor#close()} calls back into the 
interpreter, so it has
+     * to run before the interpreter is closed.
+     */
+    @Test
+    void closeReleasesInterpreterAndEnvironmentWhenActionExecutorFails() 
throws Exception {
+        PythonBridgeManager bridge = new PythonBridgeManager();
+        PythonActionExecutor actionExecutor = mock(PythonActionExecutor.class);
+        PythonInterpreter interpreter = mock(PythonInterpreter.class);
+        PythonEnvironmentManager environmentManager = 
mock(PythonEnvironmentManager.class);
+        doThrow(new IllegalStateException("action executor close failed"))
+                .when(actionExecutor)
+                .close();
+
+        setField(bridge, "pythonActionExecutor", actionExecutor);
+        setField(bridge, "pythonInterpreter", interpreter);
+        setField(bridge, "pythonEnvironmentManager", environmentManager);
+
+        assertThatThrownBy(bridge::close)
+                .isInstanceOf(IllegalStateException.class)
+                .hasMessage("action executor close failed");

Review Comment:
   Both this test and 
`ActionTaskContextManagerTest.closeClosesContinuationExecutorWhenRunnerContextFails`
 map to contract 3, which reads "unchanged in type and identity, with nothing 
suppressed".
   
   The `nothing suppressed` half isn't asserted anywhere in the PR. Every 
`getSuppressed()` assertion in it (`:128`, `ResourceCacheTest.java:308`, 
`ActionExecutionOperatorTest.java:688`) is a contract-4 non-empty check, so 
nothing pins the array as empty on a single-failure path. The identity half I 
raised earlier is covered now: `:154` uses `isSameAs`, and both paths rethrow 
through the one site at `PythonBridgeManager.java:325`.
   
   The code is fine either way, it's the table claiming a bit more than the 
tests check. Either remedy costs a line, so whichever you prefer: a 
`.satisfies(t -> assertThat(t.getSuppressed()).isEmpty())` here, or dropping 
`with nothing suppressed` from contract 3?



##########
runtime/src/main/java/org/apache/flink/agents/runtime/operator/ActionTaskContextManager.java:
##########
@@ -322,15 +323,31 @@ void removePythonAwaitableRef(ActionTask actionTask) {
 
     @Override
     public void close() throws Exception {
+        // Close the continuation executor even when the runner context fails 
to close. The first
+        // failure is rethrown with the later one suppressed.
+        //
+        // The ladder catches Throwable, not Exception, so a non-Exception 
Throwable from the
+        // runner context cannot strand the executor's thread pool. Neither 
type implements
+        // AutoCloseable, so the aggregation is spelled out rather than 
delegated.
+        Throwable firstFailure = null;
         if (runnerContext != null) {
             try {
                 runnerContext.close();
+            } catch (Throwable t) {
+                firstFailure = t;

Review Comment:
   nit: this is the one aggregation point in the PR that doesn't go through 
`ExceptionUtils.firstOrSuppressed`. It's equivalent today, since `firstFailure` 
is still null from `:332`, but it fails open: a close added above it later 
would have its failure overwritten here rather than suppressed. Any objection 
to `firstFailure = ExceptionUtils.firstOrSuppressed(t, firstFailure);` for 
consistency with the other six?



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