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


##########
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:
   You're right, and I'd rather pin the claim than trim it — fixed in 0c4df632.
   
   `.satisfies(t -> assertThat(t.getSuppressed()).isEmpty())` is now on every 
single-failure path, not just the two contract-3 tests: this one, 
`ActionTaskContextManagerTest.closeClosesContinuationExecutorWhenRunnerContextFails`,
 both `Error` tests, and 
`ActionExecutionOperatorTest.closeClosesEveryComponentWhenAnEarlierCloseFails` 
— the last being the case where the assertion says something slightly 
different, namely that a healthy `super.close()` contributes nothing.
   
   One caveat I should state rather than let the table imply otherwise: these 
are fidelity guards, not regression tests. I tried to construct a mutation of 
the current ladders that produces a spurious suppressed entry on a 
single-failure path and could not — `firstOrSuppressed` guards 
self-suppression, and a duplicated rung rethrows the same instance, so the 
guard absorbs it. They pin what contract 3 claims; they do not catch a defect 
that exists today. I've noted that in the PR body alongside the same caveat for 
`closeReportsFirstResourceFailureWithLaterOnesSuppressed`.
   
   Thanks for reading the table against the tests this carefully — that's twice 
now it was claiming more than it checked.
   



##########
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:
   No objection — done in 0c4df632, and I think it's more than a nit.
   
   Your reasoning is exactly right on both halves, and I checked the second 
half rather than assuming it: reverting the change leaves all ten tests in 
`ActionTaskContextManagerTest` green, so it is provably a no-op today and no 
test can distinguish it. It's a fail-open guard, so I added a comment recording 
why the redundant call is deliberate — otherwise it reads like something to 
simplify away.
   
   That was the one aggregation point of seven not going through 
`firstOrSuppressed`, so they're now uniform.
   
   Looking at that site for the overwrite case you describe, I noticed it had 
no two-failure test at all — the aggregation there was entirely unpinned, at 
either rung. Added `closeReportsFirstFailureWithLaterOneSuppressed`, which 
fails if the second rung overwrites instead of suppressing. That doesn't cover 
the rung you flagged, since the failure it would drop cannot exist yet, but it 
does mean the site is no longer relying on the other ladders' tests for its 
aggregation behavior.
   



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