[ 
https://issues.apache.org/jira/browse/SPARK-58904?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Jungtaek Lim updated SPARK-58904:
---------------------------------
    Summary: Execution fails to construct a state-value watermark predicate for 
valid one-sided stream-stream range joins  (was: Analyzer accepts stream-stream 
range joins that cannot evict the required state)

> Execution fails to construct a state-value watermark predicate for valid 
> one-sided stream-stream range joins
> ------------------------------------------------------------------------------------------------------------
>
>                 Key: SPARK-58904
>                 URL: https://issues.apache.org/jira/browse/SPARK-58904
>             Project: Spark
>          Issue Type: Bug
>          Components: Structured Streaming
>    Affects Versions: 4.3.0
>            Reporter: Ganesha S
>            Priority: Critical
>              Labels: correctness
>
> h3. Description
> For stream-stream joins with event-time range conditions, the analyzer may 
> accept a one-sided watermark because it can derive a state-value watermark 
> for the opposite input from the join condition.
> Execution does not always implement that accepted plan. 
> {{getOneSideStateWatermarkPredicate}} only creates a state-value eviction 
> predicate when the state side itself contains an attribute marked with 
> watermark metadata. When only the opposite input is
> watermarked, no predicate is created for the state that should be evicted.
> This affects the following directions:
> ||Join type||Watermarked input||State that should be evicted||Effect||
> |Left outer|Right|Left|Unmatched left rows are never emitted|
> |Left semi|Right|Left|Never-matched left rows remain indefinitely|
> |Right outer|Left|Right|Unmatched right rows are never emitted|
> |Full outer|Right|Left|Unmatched left rows are never emitted|
> |Full outer|Left|Right|Unmatched right rows are never emitted|
> For full outer joins, one-sided watermarking is intentional: it should 
> finalize unmatched rows from the opposite side. A watermark on both inputs is 
> needed to finalize both directions and clean up both state stores. The bug is 
> that the range-condition
> implementation does not honor the supported one-sided case.
> The state-key watermark path, where event time or a window is part of the 
> equi-join keys, is not covered by this issue.
> h3. Reproducer
> The following test uses a right-side watermark and a range condition. Once 
> the right watermark passes the latest possible matching time for the left 
> row, the left outer join should emit that row with a null right side.
> {code:java}
>   test("left outer range join with only right watermark evicts left state") {
>     val leftInput = MemoryStream[(Int, Int)]
>     val rightInput = MemoryStream[(Int, Int)]
>     val left = leftInput.toDF().toDF("leftKey", "time")
>       .select(
>         $"leftKey",
>         timestamp_seconds($"time").as("leftTime"))
>     val right = rightInput.toDF().toDF("rightKey", "time")
>       .select(
>         $"rightKey",
>         timestamp_seconds($"time").as("rightTime"))
>       .withWatermark("rightTime", "10 seconds")
>     val joined = left.join(
>       right,
>       expr(
>         """
>           |leftKey = rightKey AND
>           |leftTime BETWEEN
>           |  rightTime - INTERVAL 5 SECONDS AND
>           |  rightTime + INTERVAL 5 SECONDS
>         """.stripMargin),
>       "left_outer")
>       .select(
>         $"leftKey",
>         $"rightKey",
>         $"leftTime".cast("long"),
>         $"rightTime".cast("long"))
>     testStream(joined)(
>       AddData(leftInput, (1, 5)),
>       CheckNewAnswer(),
>       // Advances the right watermark to 20.
>       AddData(rightInput, (2, 30)),
>       CheckNewAnswer(),
>       // Runs another batch using watermark 20. A future right row that
>       // could match leftTime=5 would have rightTime in [0, 10], so the
>       // left row can now be finalized.
>       AddData(rightInput, (2, 40)),
>       // Fails because the query produces no output.
>       CheckNewAnswer(Row(1, null, 5, null))
>     )
>   }
>   {code}
> Changing the join type to {{full_outer}} reproduces the same missing 
> unmatched-left output. Swapping the inputs and using {{right_outer}} 
> reproduces the symmetric problem.
> h3. Expected behavior
> For an accepted range join, execution should construct the state-value 
> eviction predicate implied by the opposite input's watermark and the range 
> condition.
> In the reproducer, the left row should be emitted as:
> {noformat}
>   Row(1, null, 5, null)
>   {noformat}
> h3. Actual behavior
> The query passes analysis, but no left-state eviction predicate is created. 
> The left row remains in state and its unmatched outer result is never emitted.
> For left semi joins, the equivalent failure causes never-matched left rows to 
> accumulate rather than causing missing output.
> h3. Notes
> [PR #58238|https://github.com/apache/spark/pull/58238] attempted a broader 
> analyzer restriction but was closed for further design review. The fix should 
> preserve the documented one-sided watermark semantics or explicitly redefine 
> them; analysis and execution must agree.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to