This is an automated email from the ASF dual-hosted git repository.

MartijnVisser pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink.git


The following commit(s) were added to refs/heads/master by this push:
     new 7bd618a7910 [FLINK-40571][table-planner] Fix flaky 
process-stateful-multi-input-with-timeout PTF test (#29352)
7bd618a7910 is described below

commit 7bd618a7910b21f010f107fb5c8347ac1c1f4e40
Author: Purushottam Sinha <[email protected]>
AuthorDate: Mon Oct 12 15:08:38 2026 +0530

    [FLINK-40571][table-planner] Fix flaky 
process-stateful-multi-input-with-timeout PTF test (#29352)
    
    * [FLINK-40571][table-planner] Fix flaky 
process-stateful-multi-input-with-timeout PTF test
    
    The two inputs of the PTF are processed in arrival order, not event-time
    order. TimedJoinFunction keeps a single score per key, so if the city row
    arrived after several scores, the earlier scores were overwritten and only
    one join row was emitted at the city row's timestamp. The pinned output
    only matched the city-first interleaving.
    
    Restructure the test data so the output is independent of the
    interleaving: a key with a city has a single score with the same timestamp
    as its city, while keys without a city may have multiple scores. Timeouts
    stay deterministic as timers only fire once both inputs have finished.
    
    Generated-by: Claude Code (claude-opus-5-5)
    
    * [FLINK-40571][table-planner] Add single-input PTF test for clearAllTimers 
keeping state
    
    The reworked multi-input timeout test no longer has a key receiving an
    event after its join, so nothing verified that ctx.clearAllTimers()
    leaves state intact. Add a dedicated single-input program for it and
    inline the city source into the only program that uses it.
    
    Generated-by: Claude Code (claude-opus-5-5)
---
 .../stream/ProcessTableFunctionSemanticTests.java  |  1 +
 .../stream/ProcessTableFunctionTestPrograms.java   | 69 +++++++++++++++++++---
 .../exec/stream/ProcessTableFunctionTestUtils.java | 31 ++++++----
 3 files changed, 82 insertions(+), 19 deletions(-)

diff --git 
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/ProcessTableFunctionSemanticTests.java
 
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/ProcessTableFunctionSemanticTests.java
index d8f5e8294b8..bbf6a8644c2 100644
--- 
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/ProcessTableFunctionSemanticTests.java
+++ 
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/ProcessTableFunctionSemanticTests.java
@@ -107,6 +107,7 @@ public class ProcessTableFunctionSemanticTests extends 
SemanticTestBase {
                 
ProcessTableFunctionTestPrograms.PROCESS_INVALID_BROADCAST_STATE_WRITE,
                 
ProcessTableFunctionTestPrograms.PROCESS_MULTI_INPUT_WITH_SCALAR_ARGS,
                 
ProcessTableFunctionTestPrograms.PROCESS_STATEFUL_MULTI_INPUT_WITH_TIMEOUT,
+                
ProcessTableFunctionTestPrograms.PROCESS_CLEAR_ALL_TIMERS_KEEPS_STATE,
                 ProcessTableFunctionTestPrograms.PROCESS_UPDATING_MULTI_INPUT,
                 ProcessTableFunctionTestPrograms.PROCESS_ORDER_BY,
                 ProcessTableFunctionTestPrograms.PROCESS_MULTI_INPUT_ORDER_BY,
diff --git 
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/ProcessTableFunctionTestPrograms.java
 
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/ProcessTableFunctionTestPrograms.java
index d91847a1710..9c7ebe85980 100644
--- 
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/ProcessTableFunctionTestPrograms.java
+++ 
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/ProcessTableFunctionTestPrograms.java
@@ -29,6 +29,7 @@ import 
org.apache.flink.table.planner.plan.nodes.exec.stream.ProcessTableFunctio
 import 
org.apache.flink.table.planner.plan.nodes.exec.stream.ProcessTableFunctionTestUtils.BroadcastTimersFunction;
 import 
org.apache.flink.table.planner.plan.nodes.exec.stream.ProcessTableFunctionTestUtils.ChainedReceivingFunction;
 import 
org.apache.flink.table.planner.plan.nodes.exec.stream.ProcessTableFunctionTestUtils.ChainedSendingFunction;
+import 
org.apache.flink.table.planner.plan.nodes.exec.stream.ProcessTableFunctionTestUtils.ClearAllTimersKeepsStateFunction;
 import 
org.apache.flink.table.planner.plan.nodes.exec.stream.ProcessTableFunctionTestUtils.ClearStateFunction;
 import 
org.apache.flink.table.planner.plan.nodes.exec.stream.ProcessTableFunctionTestUtils.ComplexValueViewFunction;
 import 
org.apache.flink.table.planner.plan.nodes.exec.stream.ProcessTableFunctionTestUtils.ContextFunction;
@@ -118,7 +119,6 @@ import static 
org.apache.flink.table.planner.plan.nodes.exec.stream.ProcessTable
 import static 
org.apache.flink.table.planner.plan.nodes.exec.stream.ProcessTableFunctionTestUtils.PASS_THROUGH_BASE_SINK_SCHEMA;
 import static 
org.apache.flink.table.planner.plan.nodes.exec.stream.ProcessTableFunctionTestUtils.TIMED_BASE_SINK_SCHEMA;
 import static 
org.apache.flink.table.planner.plan.nodes.exec.stream.ProcessTableFunctionTestUtils.TIMED_BROADCAST_RULES_SOURCE;
-import static 
org.apache.flink.table.planner.plan.nodes.exec.stream.ProcessTableFunctionTestUtils.TIMED_CITY_SOURCE;
 import static 
org.apache.flink.table.planner.plan.nodes.exec.stream.ProcessTableFunctionTestUtils.TIMED_MULTI_BASE_SINK_SCHEMA;
 import static 
org.apache.flink.table.planner.plan.nodes.exec.stream.ProcessTableFunctionTestUtils.TIMED_SOURCE;
 import static 
org.apache.flink.table.planner.plan.nodes.exec.stream.ProcessTableFunctionTestUtils.TIMED_SOURCE_LATE_EVENTS;
@@ -1979,19 +1979,42 @@ public class ProcessTableFunctionTestPrograms {
                             "process-stateful-multi-input-with-timeout",
                             "joins two tables and emits the left side after a 
timeout if there is no right side")
                     .setupTemporarySystemFunction("f", TimedJoinFunction.class)
-                    .setupTableSource(TIMED_SOURCE)
-                    .setupTableSource(TIMED_CITY_SOURCE)
+                    // The two inputs may reach eval() in any interleaving. To 
keep the output
+                    // independent of it, a key with a city has a single score 
with the same
+                    // timestamp as its city. Keys without a city may have 
multiple scores.
+                    .setupTableSource(
+                            SourceTestStep.newBuilder("t")
+                                    .addSchema(TIMED_SOURCE_SCHEMA)
+                                    .producedValues(
+                                            Row.of("Bob", 1, 
Instant.ofEpochMilli(0)),
+                                            Row.of("Alice", 1, 
Instant.ofEpochMilli(1)),
+                                            Row.of("Charly", 3, 
Instant.ofEpochMilli(2)),
+                                            Row.of("Alice", 2, 
Instant.ofEpochMilli(3)),
+                                            Row.of("Dave", 4, 
Instant.ofEpochMilli(4)),
+                                            Row.of("Frank", 5, 
Instant.ofEpochMilli(6)))
+                                    .build())
+                    .setupTableSource(
+                            SourceTestStep.newBuilder("city")
+                                    .addSchema(
+                                            "name STRING",
+                                            "city STRING",
+                                            "ts TIMESTAMP_LTZ(3)",
+                                            "WATERMARK FOR ts AS ts - INTERVAL 
'0.001' SECOND")
+                                    .producedValues(
+                                            Row.of("Bob", "London", 
Instant.ofEpochMilli(0)),
+                                            Row.of("Charly", "Paris", 
Instant.ofEpochMilli(2)),
+                                            Row.of("Dave", "Berlin", 
Instant.ofEpochMilli(4)),
+                                            Row.of("Eve", "Rome", 
Instant.ofEpochMilli(5)))
+                                    .build())
                     .setupTableSink(
                             SinkTestStep.newBuilder("sink")
                                     .addSchema(TIMED_MULTI_BASE_SINK_SCHEMA)
                                     .consumedValues(
                                             "+I[Bob, Bob, 1 score in city 
London, 1970-01-01T00:00:00Z]",
-                                            "+I[Bob, Bob, 2 score in city 
London, 1970-01-01T00:00:00.002Z]",
-                                            "+I[Bob, Bob, 3 score in city 
London, 1970-01-01T00:00:00.003Z]",
-                                            "+I[Bob, Bob, 4 score in city 
London, 1970-01-01T00:00:00.004Z]",
-                                            "+I[Bob, Bob, 5 score in city 
London, 1970-01-01T00:00:00.005Z]",
-                                            "+I[Bob, Bob, 6 score in city 
London, 1970-01-01T00:00:00.006Z]",
-                                            "+I[Alice, Alice, no city found 
for score 1, 1970-01-01T00:00:01.001Z]")
+                                            "+I[Charly, Charly, 3 score in 
city Paris, 1970-01-01T00:00:00.002Z]",
+                                            "+I[Dave, Dave, 4 score in city 
Berlin, 1970-01-01T00:00:00.004Z]",
+                                            "+I[Alice, Alice, no city found 
for score 2, 1970-01-01T00:00:01.003Z]",
+                                            "+I[Frank, Frank, no city found 
for score 5, 1970-01-01T00:00:01.006Z]")
                                     .build())
                     .runSql(
                             "INSERT INTO sink SELECT * FROM f("
@@ -2000,6 +2023,34 @@ public class ProcessTableFunctionTestPrograms {
                                     + "on_time => DESCRIPTOR(ts))")
                     .build();
 
+    public static final TableTestProgram PROCESS_CLEAR_ALL_TIMERS_KEEPS_STATE =
+            TableTestProgram.of(
+                            "process-clear-all-timers-keeps-state",
+                            "clearing all timers does not clear the state")
+                    .setupTemporarySystemFunction("f", 
ClearAllTimersKeepsStateFunction.class)
+                    .setupTableSource(
+                            SourceTestStep.newBuilder("t")
+                                    .addSchema(TIMED_SOURCE_SCHEMA)
+                                    .producedValues(
+                                            Row.of("Bob", 1, 
Instant.ofEpochMilli(0)),
+                                            Row.of("Alice", 1, 
Instant.ofEpochMilli(1)),
+                                            Row.of("Bob", 2, 
Instant.ofEpochMilli(2)),
+                                            Row.of("Bob", 3, 
Instant.ofEpochMilli(3)))
+                                    .build())
+                    .setupTableSink(
+                            SinkTestStep.newBuilder("sink")
+                                    .addSchema(KEYED_TIMED_BASE_SINK_SCHEMA)
+                                    .consumedValues(
+                                            "+I[Bob, count 1, 
1970-01-01T00:00:00Z]",
+                                            "+I[Alice, count 1, 
1970-01-01T00:00:00.001Z]",
+                                            "+I[Bob, count 2, 
1970-01-01T00:00:00.002Z]",
+                                            "+I[Bob, count 3, 
1970-01-01T00:00:00.003Z]",
+                                            "+I[Alice, timeout with count 1, 
1970-01-01T00:00:01.001Z]")
+                                    .build())
+                    .runSql(
+                            "INSERT INTO sink SELECT * FROM f(r => TABLE t 
PARTITION BY name, on_time => DESCRIPTOR(ts))")
+                    .build();
+
     public static final TableTestProgram PROCESS_UPDATING_MULTI_INPUT =
             TableTestProgram.of(
                             "process-updating-multi-input",
diff --git 
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/ProcessTableFunctionTestUtils.java
 
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/ProcessTableFunctionTestUtils.java
index 4e247a92a8a..1e5dad159c3 100644
--- 
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/ProcessTableFunctionTestUtils.java
+++ 
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/ProcessTableFunctionTestUtils.java
@@ -100,16 +100,6 @@ public class ProcessTableFunctionTestUtils {
             "CREATE VIEW city AS SELECT * FROM "
                     + "(VALUES ('Bob', 'London'), ('Alice', 'Berlin'), 
('Charly', 'Paris')) AS T(name, city)";
 
-    public static final SourceTestStep TIMED_CITY_SOURCE =
-            SourceTestStep.newBuilder("city")
-                    .addSchema(
-                            "name STRING",
-                            "city STRING",
-                            "ts TIMESTAMP_LTZ(3)",
-                            "WATERMARK FOR ts AS ts - INTERVAL '0.001' SECOND")
-                    .producedValues(Row.of("Bob", "London", 
Instant.ofEpochMilli(0)))
-                    .build();
-
     public static final String UPDATING_VALUES =
             "CREATE VIEW t AS SELECT name, COUNT(*) FROM "
                     + "(VALUES ('Bob', 12), ('Alice', 42), ('Bob', 14)) AS 
T(name, score) "
@@ -1254,6 +1244,27 @@ public class ProcessTableFunctionTestUtils {
         }
     }
 
+    /** Testing function. */
+    public static class ClearAllTimersKeepsStateFunction extends 
AppendProcessTableFunctionBase {
+        public void eval(
+                Context ctx,
+                @StateHint Tuple1<Integer> count,
+                @ArgumentHint({SET_SEMANTIC_TABLE, REQUIRE_ON_TIME}) Row r) {
+            final TimeContext<Instant> timeCtx = 
ctx.timeContext(Instant.class);
+            count.f0 = count.f0 == null ? 1 : count.f0 + 1;
+            if (count.f0 == 1) {
+                timeCtx.registerOnTime("timeout", 
timeCtx.time().plusMillis(1000));
+            } else if (count.f0 == 2) {
+                ctx.clearAllTimers();
+            }
+            collect(Row.of("count " + count.f0));
+        }
+
+        public void onTimer(OnTimerContext ctx, Tuple1<Integer> count) {
+            collect(Row.of("timeout with count " + count.f0));
+        }
+    }
+
     /**
      * Implements a custom join that acts like kind of an outer join and never 
produces deletions.
      * Both the score and city can change at any time. The join will output an 
update if a matching

Reply via email to