@@ -26,6 +26,8 @@ import java.util.{Date, Locale}
26
26
import scala .concurrent .duration ._
27
27
import scala .language .implicitConversions
28
28
29
+ import org .apache .spark .sql .delta .DeltaConfigs .IN_COMMIT_TIMESTAMPS_ENABLED
30
+
29
31
import com .databricks .spark .util .Log4jUsageLogger
30
32
import org .apache .spark .sql .delta .DeltaHistoryManagerSuiteShims ._
31
33
import org .apache .spark .sql .delta .DeltaTestUtils .createTestAddFile
@@ -660,6 +662,54 @@ abstract class DeltaHistoryManagerBase extends DeltaTimeTravelTests
660
662
testGetHistory(start = 2 , endOpt = Some (1 ), versions = Seq .empty, expectedLogUpdates = 0 )
661
663
}
662
664
}
665
+
666
+ test(" getCommitFromNonICTRange should handle empty history by throwing proper error" ) {
667
+ val tblName = " delta_table"
668
+ withTable(tblName) {
669
+ val start = 1540415658000L
670
+ generateCommits(tblName, start)
671
+ val deltaLog = DeltaLog .forTable(spark, getTableLocation(tblName))
672
+
673
+ val deltaFile = new File (FileNames .unsafeDeltaFile(deltaLog.logPath, 0 ).toUri)
674
+ assert(deltaFile.delete(), " Failed to delete delta log file" )
675
+
676
+ val e = intercept[DeltaAnalysisException ] {
677
+ deltaLog.history.getCommitFromNonICTRange(0 , 1 , start)
678
+ }
679
+
680
+ assert(e.getMessage.contains(" DELTA_NO_COMMITS_FOUND" ))
681
+ assert(e.getMessage.contains(deltaLog.logPath.toString))
682
+ }
683
+ }
684
+
685
+ test(" parallel search handles empty commits in a partition correctly" ) {
686
+ if (coordinatedCommitsBackfillBatchSize.isDefined) {
687
+ cancel(" This test is not compatible with coordinated commits backfill timestamps." )
688
+ }
689
+ val tblName = " delta_table"
690
+ withTable(tblName) {
691
+ // Small threshold to trigger parallel search
692
+ withSQLConf(
693
+ DeltaSQLConf .DELTA_HISTORY_PAR_SEARCH_THRESHOLD .key -> " 3" ,
694
+ IN_COMMIT_TIMESTAMPS_ENABLED .key -> " false" ) {
695
+ val start = 1540415658000L
696
+ // Generate 10 commits which will be processed in parallel due to threshold=5
697
+ val timestamps = (0 to 9 ).map(i => start + (i * 20 ).minutes)
698
+ generateCommits(tblName, timestamps : _* )
699
+ val table = DeltaTableV2 (spark, TableIdentifier (tblName))
700
+ val deltaLog = table.deltaLog
701
+
702
+ // Delete all files in first partition to simulate concurrent metadata cleanup
703
+ val deltaFiles = (0 to 4 ).map { version =>
704
+ new File (FileNames .unsafeDeltaFile(deltaLog.logPath, version).toUri)
705
+ }
706
+ deltaFiles.foreach(f =>
707
+ assert(f.delete(), s " Failed to delete delta log file ${f.getPath}" ))
708
+ assert(
709
+ deltaLog.history.getCommitFromNonICTRange(0 , 9 , start + (7 * 20 ).minutes).version == 7 )
710
+ }
711
+ }
712
+ }
663
713
}
664
714
665
715
/** Uses V2 resolution code paths */
0 commit comments