This is an automated email from the ASF dual-hosted git repository.
wenjin272 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/flink-agents.git
The following commit(s) were added to refs/heads/main by this push:
new 58801617 [hotfix] Propagate action task Errors from mailbox (#880)
58801617 is described below
commit 588016170be176dab269721bfcd64bbe4c6351e3
Author: Wenjin Xie <[email protected]>
AuthorDate: Wed Jul 8 15:08:10 2026 +0800
[hotfix] Propagate action task Errors from mailbox (#880)
Catch Throwable in mailbox-submitted action task processing so Errors are
rethrown through the mailbox and fail the task visibly instead of remaining in
a Future while processing keys stay in-flight.
Co-authored-by: Codex <[email protected]>
---
.../runtime/operator/ActionExecutionOperator.java | 6 ++-
.../operator/ActionExecutionOperatorTest.java | 58 ++++++++++++++++++++++
2 files changed, 62 insertions(+), 2 deletions(-)
diff --git
a/runtime/src/main/java/org/apache/flink/agents/runtime/operator/ActionExecutionOperator.java
b/runtime/src/main/java/org/apache/flink/agents/runtime/operator/ActionExecutionOperator.java
index 665551bb..46d09e0a 100644
---
a/runtime/src/main/java/org/apache/flink/agents/runtime/operator/ActionExecutionOperator.java
+++
b/runtime/src/main/java/org/apache/flink/agents/runtime/operator/ActionExecutionOperator.java
@@ -283,12 +283,14 @@ public class ActionExecutionOperator<IN, OUT> extends
AbstractStreamOperator<OUT
private void tryProcessActionTaskForKey(Object key) {
try {
processActionTaskForKey(key);
- } catch (Exception e) {
+ } catch (Throwable t) {
+ // MailboxExecutor.submit() stores task failures in its Future.
Catch Throwable and
+ // rethrow via execute() so Errors fail the task instead of
leaving the key in-flight.
mailboxExecutor.execute(
() ->
ExceptionUtils.rethrow(
new ActionTaskExecutionException(
- "Failed to execute action task",
e)),
+ "Failed to execute action task",
t)),
"throw exception in mailbox");
}
}
diff --git
a/runtime/src/test/java/org/apache/flink/agents/runtime/operator/ActionExecutionOperatorTest.java
b/runtime/src/test/java/org/apache/flink/agents/runtime/operator/ActionExecutionOperatorTest.java
index 9ac18e3e..22c0072f 100644
---
a/runtime/src/test/java/org/apache/flink/agents/runtime/operator/ActionExecutionOperatorTest.java
+++
b/runtime/src/test/java/org/apache/flink/agents/runtime/operator/ActionExecutionOperatorTest.java
@@ -289,6 +289,27 @@ public class ActionExecutionOperatorTest {
}
}
+ @Test
+ void testMailboxSubmittedActionTaskPropagatesError() throws Exception {
+ try (KeyedOneInputStreamOperatorTestHarness<Long, Long, Object>
testHarness =
+ new KeyedOneInputStreamOperatorTestHarness<>(
+ new ActionExecutionOperatorFactory(
+ TestAgent.getLinkageErrorAgentPlan(), true),
+ (KeySelector<Long, Long>) value -> value,
+ TypeInformation.of(Long.class))) {
+ testHarness.open();
+ ActionExecutionOperator<Long, Object> operator =
+ (ActionExecutionOperator<Long, Object>)
testHarness.getOperator();
+
+ testHarness.processElement(new StreamRecord<>(0L));
+ assertThatThrownBy(() -> operator.waitInFlightEventsFinished())
+
.hasCauseInstanceOf(ActionExecutionOperator.ActionTaskExecutionException.class)
+ .rootCause()
+ .isInstanceOf(NoClassDefFoundError.class)
+ .hasMessageContaining("synthetic missing runtime
dependency");
+ }
+ }
+
@Test
void testInMemoryActionStateStoreIntegration() throws Exception {
AgentPlan agentPlanWithStateStore = TestAgent.getAgentPlan(false);
@@ -1711,6 +1732,20 @@ public class ActionExecutionOperatorTest {
}
}
+ public static class LinkageErrorAction {
+ static {
+ if (shouldThrowLinkageError()) {
+ throw new NoClassDefFoundError("synthetic missing runtime
dependency");
+ }
+ }
+
+ private static boolean shouldThrowLinkageError() {
+ return true;
+ }
+
+ public static void action(Event event, RunnerContext context) {}
+ }
+
public static void resetReconcilableRecoveryFixture() {
RECONCILABLE_CALL_COUNTER.set(0);
RECONCILABLE_RECONCILE_COUNTER.set(0);
@@ -1937,6 +1972,29 @@ public class ActionExecutionOperatorTest {
return null;
}
+ public static AgentPlan getLinkageErrorAgentPlan() {
+ try {
+ Map<String, List<Action>> actionsByEvent = new HashMap<>();
+ Map<String, Action> actions = new HashMap<>();
+
+ Action errorAction =
+ new Action(
+ "linkageErrorAction",
+ new JavaFunction(
+ LinkageErrorAction.class,
+ "action",
+ new Class<?>[] {Event.class,
RunnerContext.class}),
+
Collections.singletonList(InputEvent.EVENT_TYPE));
+ actionsByEvent.put(InputEvent.EVENT_TYPE,
Collections.singletonList(errorAction));
+ actions.put(errorAction.getName(), errorAction);
+
+ return new AgentPlan(actions, actionsByEvent, new HashMap<>());
+ } catch (Exception e) {
+ ExceptionUtils.rethrow(e);
+ }
+ return null;
+ }
+
// ==================== Actions for Exception Recovery Tests
====================
/**