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 df18224bb [runtime] Reject RuntimeExecutionMode.BATCH with the default 
batch state backend (#1104)
df18224bb is described below

commit df18224bb9cedd5e37d8c694d29e058354301d06
Author: Ashfaq <[email protected]>
AuthorDate: Mon Sep 14 09:32:20 2026 +0530

    [runtime] Reject RuntimeExecutionMode.BATCH with the default batch state 
backend (#1104)
    
    Generated-by: Claude Code 2.1.226 (Claude Sonnet 5)
---
 .../apache/flink/agents/runtime/CompileUtils.java  | 41 ++++++++++++++++++++++
 .../flink/agents/runtime/CompileUtilsTest.java     | 34 ++++++++++++++++++
 2 files changed, 75 insertions(+)

diff --git 
a/runtime/src/main/java/org/apache/flink/agents/runtime/CompileUtils.java 
b/runtime/src/main/java/org/apache/flink/agents/runtime/CompileUtils.java
index c1bf0805c..65e998cb8 100644
--- a/runtime/src/main/java/org/apache/flink/agents/runtime/CompileUtils.java
+++ b/runtime/src/main/java/org/apache/flink/agents/runtime/CompileUtils.java
@@ -22,15 +22,19 @@ import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import org.apache.flink.agents.plan.AgentPlan;
 import org.apache.flink.agents.runtime.operator.ActionExecutionOperatorFactory;
+import org.apache.flink.api.common.RuntimeExecutionMode;
 import org.apache.flink.api.common.typeinfo.TypeInformation;
 import org.apache.flink.api.java.functions.KeySelector;
 import org.apache.flink.api.java.typeutils.RowTypeInfo;
+import org.apache.flink.configuration.ExecutionOptions;
+import org.apache.flink.configuration.ReadableConfig;
 import org.apache.flink.streaming.api.datastream.DataStream;
 import org.apache.flink.streaming.api.datastream.KeyedStream;
 import org.apache.flink.streaming.api.typeinfo.python.PickledByteArrayTypeInfo;
 import org.apache.flink.types.Row;
 
 import static org.apache.flink.util.Preconditions.checkArgument;
+import static org.apache.flink.util.Preconditions.checkState;
 
 /** A utility class that bridges Flink DataStream/SQL with the Flink Agents 
agent. */
 public class CompileUtils {
@@ -97,6 +101,7 @@ public class CompileUtils {
             TypeInformation<OUT> outTypeInformation,
             boolean inputIsJava,
             boolean pythonKeyIsPickled) {
+        checkBatchStateBackendCompatibility(keyedInputStream);
         return (DataStream<OUT>)
                 keyedInputStream
                         .transform(
@@ -107,6 +112,42 @@ public class CompileUtils {
                         .setParallelism(keyedInputStream.getParallelism());
     }
 
+    /**
+     * Rejects a job configuration known to silently drop records: explicit 
{@code
+     * RuntimeExecutionMode.BATCH} together with Flink's batch-specific 
keyed-state backend (enabled
+     * by default in that mode).
+     *
+     * <p>{@link ActionExecutionOperatorFactory}'s operator keeps pending 
action tasks in keyed
+     * state across mailbox continuations that can span multiple keys of the 
same subtask. Flink's
+     * batch keyed-state backend assumes a key is fully processed before 
moving to the next one and
+     * clears keyed state on every key switch, so a continuation for one key 
can be silently
+     * discarded by the operator moving on to process another key first. The 
job still reports
+     * {@code FINISHED}; only the affected records go missing. See
+     * https://github.com/apache/flink-agents/issues/939.
+     *
+     * <p>This only catches an explicitly configured {@code BATCH} runtime 
mode. {@code
+     * RuntimeExecutionMode.AUTOMATIC} (the default) that later resolves to 
batch execution because
+     * every source happens to be bounded is not detected here, since that 
resolution has not
+     * happened yet at graph-construction time.
+     */
+    private static void checkBatchStateBackendCompatibility(KeyedStream<?, ?> 
keyedInputStream) {
+        ReadableConfig configuration =
+                keyedInputStream.getExecutionEnvironment().getConfiguration();
+        boolean isExplicitBatchMode =
+                configuration.get(ExecutionOptions.RUNTIME_MODE) == 
RuntimeExecutionMode.BATCH;
+        boolean usesBatchStateBackend = 
configuration.get(ExecutionOptions.USE_BATCH_STATE_BACKEND);
+        checkState(
+                !isExplicitBatchMode || !usesBatchStateBackend,
+                "Flink Agents does not support RuntimeExecutionMode.BATCH with 
Flink's batch"
+                        + " keyed-state backend 
(execution.batch-state-backend.enabled, enabled by"
+                        + " default in BATCH mode): pending action-task state 
can be silently"
+                        + " discarded when the operator processes another key 
before a key's"
+                        + " workflow finishes, causing records to be dropped 
without any error."
+                        + " Either run this job with 
RuntimeExecutionMode.STREAMING, or set"
+                        + " execution.batch-state-backend.enabled to false. 
See"
+                        + " https://github.com/apache/flink-agents/issues/939 
for details.");
+    }
+
     /** Returns whether a PyFlink Row field uses its default pickle 
representation. */
     static boolean isPickledPythonFieldType(TypeInformation<?> 
typeInformation, int fieldIndex) {
         checkArgument(fieldIndex >= 0, "Field index must not be negative, but 
got %s", fieldIndex);
diff --git 
a/runtime/src/test/java/org/apache/flink/agents/runtime/CompileUtilsTest.java 
b/runtime/src/test/java/org/apache/flink/agents/runtime/CompileUtilsTest.java
index 96a0ce8fc..b2dba2ea2 100644
--- 
a/runtime/src/test/java/org/apache/flink/agents/runtime/CompileUtilsTest.java
+++ 
b/runtime/src/test/java/org/apache/flink/agents/runtime/CompileUtilsTest.java
@@ -20,9 +20,12 @@ package org.apache.flink.agents.runtime;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import org.apache.flink.agents.plan.AgentPlan;
 import org.apache.flink.agents.runtime.operator.ActionExecutionOperatorTest;
+import org.apache.flink.api.common.RuntimeExecutionMode;
 import org.apache.flink.api.common.typeinfo.TypeInformation;
 import org.apache.flink.api.common.typeinfo.Types;
 import org.apache.flink.api.java.functions.KeySelector;
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.configuration.ExecutionOptions;
 import org.apache.flink.streaming.api.datastream.DataStream;
 import org.apache.flink.streaming.api.datastream.DataStreamSource;
 import org.apache.flink.streaming.api.datastream.KeyedStream;
@@ -39,6 +42,8 @@ import java.util.List;
 import java.util.stream.Collectors;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 /** Tests for {@link CompileUtils}. */
@@ -103,6 +108,35 @@ public class CompileUtilsTest {
         checkResult(resultList);
     }
 
+    @Test
+    void rejectsExplicitBatchModeWithDefaultBatchStateBackend() {
+        // execution.batch-state-backend.enabled defaults to true in BATCH 
mode, which is exactly
+        // the combination that silently drops records (issue #939).
+        Configuration conf = new Configuration();
+        conf.set(ExecutionOptions.RUNTIME_MODE, RuntimeExecutionMode.BATCH);
+        StreamExecutionEnvironment env = 
StreamExecutionEnvironment.getExecutionEnvironment(conf);
+        KeyedStream<Long, Long> keyedInputStream = 
env.fromData(testSequence).keyBy(x -> x);
+
+        assertThatIllegalStateException()
+                .isThrownBy(() -> 
CompileUtils.connectToAgent(keyedInputStream, TEST_AGENT_PLAN))
+                .withMessageContaining("RuntimeExecutionMode.BATCH")
+                
.withMessageContaining("execution.batch-state-backend.enabled");
+    }
+
+    @Test
+    void allowsExplicitBatchModeWithBatchStateBackendDisabled() {
+        // The documented workaround: disabling the batch-specific state 
backend restores the
+        // per-key-processed-to-completion semantics the operator relies on.
+        Configuration conf = new Configuration();
+        conf.set(ExecutionOptions.RUNTIME_MODE, RuntimeExecutionMode.BATCH);
+        conf.set(ExecutionOptions.USE_BATCH_STATE_BACKEND, false);
+        StreamExecutionEnvironment env = 
StreamExecutionEnvironment.getExecutionEnvironment(conf);
+        KeyedStream<Long, Long> keyedInputStream = 
env.fromData(testSequence).keyBy(x -> x);
+
+        assertThatCode(() -> CompileUtils.connectToAgent(keyedInputStream, 
TEST_AGENT_PLAN))
+                .doesNotThrowAnyException();
+    }
+
     @Test
     void detectsPickledAndExplicitByteArrayPythonKeyTypes() {
         assertThat(

Reply via email to