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

HyukjinKwon pushed a commit to branch branch-4.0
in repository https://gitbox.apache.org/repos/asf/spark.git


The following commit(s) were added to refs/heads/branch-4.0 by this push:
     new f1655fd60c2e [SPARK-57652][SS][TESTS] Deflake 
snapshotStartBatchId-with-transformWithState StateDataSource test
f1655fd60c2e is described below

commit f1655fd60c2ec8e01478332d1be2ed4abf232a66
Author: Hyukjin Kwon <[email protected]>
AuthorDate: Mon Jul 6 12:41:32 2026 +0900

    [SPARK-57652][SS][TESTS] Deflake 
snapshotStartBatchId-with-transformWithState StateDataSource test
    
    Replace the fixed Thread.sleep(5000) in the "snapshotStartBatchId with 
transformWithState" test of
    StateDataSourceTransformWithStateSuite with a deterministic eventually(...) 
wait that polls until the
    RocksDB snapshot files the reader needs (snapshot version 2 for the 
partitions read) have actually
    been uploaded by the asynchronous maintenance thread. The timeout assertion 
prints the state-directory
    contents so a recurrence is diagnosable. Test-only.
    
    Backport of SPARK-57652 to branch-4.0. The fix is on master (850a41e0) and 
branch-4.2/4.1 but was not
    on branch-4.0, so branch-4.0 build_maven fails on the (encoding = avro) 
case with
    FileNotFoundException .../state/0/1/2.zip.
    
    (cherry picked from commit 850a41e0623834c9b7eb5457692f08b61b8d770a)
    
    Co-authored-by: Isaac
---
 .../StateDataSourceTransformWithStateSuite.scala   | 32 ++++++++++++++++++++--
 1 file changed, 30 insertions(+), 2 deletions(-)

diff --git 
a/sql/core/src/test/scala/org/apache/spark/sql/execution/datasources/v2/state/StateDataSourceTransformWithStateSuite.scala
 
b/sql/core/src/test/scala/org/apache/spark/sql/execution/datasources/v2/state/StateDataSourceTransformWithStateSuite.scala
index 7d242c7444f1..a0a617279b8c 100644
--- 
a/sql/core/src/test/scala/org/apache/spark/sql/execution/datasources/v2/state/StateDataSourceTransformWithStateSuite.scala
+++ 
b/sql/core/src/test/scala/org/apache/spark/sql/execution/datasources/v2/state/StateDataSourceTransformWithStateSuite.scala
@@ -20,6 +20,7 @@ import java.io.File
 import java.time.Duration
 
 import org.apache.hadoop.conf.Configuration
+import org.scalatest.time.SpanSugar._
 
 import org.apache.spark.io.CompressionCodec
 import org.apache.spark.sql.{Encoders, Row}
@@ -1050,20 +1051,47 @@ class StateDataSourceTransformWithStateSuite extends 
StateStoreMetricsTest
           .transformWithState(new AggregationStatefulProcessor(),
             TimeMode.None(),
             OutputMode.Append())
+
+        // Block until the async maintenance thread has written the RocksDB 
snapshot `.zip` for the
+        // given state-store `version` (matching both `<v>.zip` and 
checkpoint-v2 `<v>_<uid>.zip`)
+        // on each of `partitions`. Used right after `version` is committed 
(while it is the current
+        // version), since maintenance only ever snapshots the current 
version. On timeout it prints
+        // the actual directory contents so a recurrence in scheduled jobs is 
diagnosable.
+        def waitForStateSnapshot(version: Long, partitions: Seq[Int]): 
StreamAction = Execute { _ =>
+          val opStateDir = new File(tmpDir, "state/0")
+          eventually(timeout(60.seconds), interval(100.milliseconds)) {
+            partitions.foreach { partition =>
+              val partitionDir = new File(opStateDir, partition.toString)
+              val files = Option(partitionDir.listFiles())
+                .map(_.map(_.getName).sorted.toSeq).getOrElse(Seq.empty)
+              val snapshotUploaded = 
files.exists(_.matches(s"$version(_.*)?\\.zip"))
+              assert(snapshotUploaded,
+                s"Snapshot (version $version) for partition $partition was not 
uploaded in time. " +
+                  s"Contents of $partitionDir: ${files.mkString("[", ", ", 
"]")}")
+            }
+          }
+        }
+
         testStream(query)(
           StartStream(checkpointLocation = tmpDir.getCanonicalPath),
           AddData(inputData, (1, 1L), (2, 2L), (3, 3L), (4, 4L)),
           ProcessAllAvailable(),
           AddData(inputData, (5, 1L), (6, 2L), (7, 3L), (8, 4L)),
           ProcessAllAvailable(),
+          // State version 2 is now the current version. The 
snapshotStartBatchId=1 reader below
+          // needs the version-2 snapshot, and the asynchronous maintenance 
thread only creates a
+          // snapshot for the *current* version. So block here, while version 
2 is still current,
+          // until that snapshot has actually been written - this 
deterministically forces it to
+          // exist. (A fixed sleep at the *end* is flaky: once later batches 
advance the current
+          // version, maintenance never goes back to snapshot version 2, so it 
may never appear and
+          // the reader fails with FileNotFoundException on `2.zip`.)
+          waitForStateSnapshot(version = 2, partitions = Seq(1, 4)),
           AddData(inputData, (9, 1L), (10, 2L), (11, 3L), (12, 4L)),
           ProcessAllAvailable(),
           AddData(inputData, (13, 1L), (14, 2L), (15, 3L), (16, 4L)),
           ProcessAllAvailable(),
           AddData(inputData, (17, 1L), (18, 2L), (19, 3L), (20, 4L)),
           ProcessAllAvailable(),
-          // Ensure that we get a chance to upload created snapshots
-          Execute { _ => Thread.sleep(5000) },
           StopStream
         )
       }


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

Reply via email to