HeartSaVioR commented on PR #60073:
URL: https://github.com/apache/spark/pull/60073#issuecomment-6631279692

   Thanks for the change - the code change itself looks OK to me.
   
   W.r.t testing, I'd rather see more detailed and controlled unit test(s) 
where we can verify that ReusedExchangeExec is taken into account "and" the 
watermark works. That detailed verification isn't something e2e golden file 
test can provide; golden file test can only compare the final output, not the 
executed plan or the state of each operator.
   
   You can use temp view to replicate very similar code with Scala unit test, 
or you could probably give a try with below code (Claude took a look at the 
test code and converted with pure DataFrame API):
   
   ```
   import org.apache.spark.sql.execution.exchange.ReusedExchangeExec
   
     Seq(true, false).foreach { reuseEnabled =>
       test("SPARK-60862: watermark propagates through reused exchange - " +
           s"exchange reuse enabled: $reuseEnabled") {
         withSQLConf(SQLConf.EXCHANGE_REUSE_ENABLED.key -> 
reuseEnabled.toString) {
           val inputData = MemoryStream[Int]
   
           // Both union branches read the same aggregated DataFrame, hence 
share the same
           // EventTimeWatermark node. With exchange reuse enabled, the shuffle 
under the second
           // branch's stateful aggregation is replaced by ReusedExchangeExec.
           val aggregated = inputData.toDF()
             .withColumn("eventTime", timestamp_seconds($"value"))
             .withWatermark("eventTime", "0 seconds")
             .groupBy(window($"eventTime", "5 seconds").as("window"), ($"value" 
% 2).as("key"))
             .agg(count("*").as("count"))
             .select($"window.start".cast("long").as("windowStart"), $"key", 
$"count")
   
           val stream = aggregated.select(lit("left").as("branch"), $"*")
             .union(aggregated.select(lit("right").as("branch"), $"*"))
             .as[(String, Long, Int, Long)]
   
           testStream(stream)(
             AddData(inputData, 0, 1, 2, 3, 4),
             // watermark advances to 4s, which does not close [0, 5) yet
             CheckNewAnswer(),
   
             AddData(inputData, 10),
             // no-data batch with watermark 10s closes [0, 5) in BOTH branches
             CheckNewAnswer(
               ("left", 0L, 0, 3L), ("left", 0L, 1, 2L),
               ("right", 0L, 0, 3L), ("right", 0L, 1, 2L)),
             AssertOnQuery { q =>
               val plan = q.lastExecution.executedPlan
               val reused = plan.collect { case r: ReusedExchangeExec => r }
               assert(reused.nonEmpty === reuseEnabled,
                 s"Expected ReusedExchangeExec only when reuse is enabled. 
Plan: $plan")
               // Both stateful operators must see the propagated watermark. 
Without the fix,
               // the operator above ReusedExchangeExec gets Some(0).
               val evictionWatermarks = plan.collect {
                 case s: StateStoreSaveExec => s.eventTimeWatermarkForEviction
               }
               assert(evictionWatermarks === Seq(Some(10000L), Some(10000L)))
               true
             },
             // only [10, 15) with key 0 remains in each operator's state
             assertNumStateRows(Seq(1, 1))
           )
         }
       }
     }
   ```
   
   Maybe an useful negative test could be following: we create a single 
streaming DataFrame and apply withWatermark two times (becomes two different 
DataFrames) and join them. They should have different watermark nodes. Arguably 
this is more related to reused exchange itself since it doesn't trigger reused 
exchange, but since we are here to address the relationship between reused 
exchange and watermark, it's still good to address while we are here.


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


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

Reply via email to