@@ -831,7 +831,8 @@ func ingestUpdateSeqNum(
831831// is returned as the splitFile.
832832func ingestTargetLevel (
833833 ctx context.Context ,
834- overlapChecker * overlapChecker ,
834+ cmp base.Compare ,
835+ lsmOverlap lsmOverlap ,
835836 baseLevel int ,
836837 compactions map [* compaction ]struct {},
837838 meta * fileMetadata ,
@@ -903,46 +904,32 @@ func ingestTargetLevel(
903904 // existing point that falls within the ingested table bounds as being "data
904905 // overlap".
905906
906- // This assertion implicitly checks that we have the current version of
907- // the metadata.
908- if overlapChecker .v .L0Sublevels == nil {
909- return 0 , nil , base .AssertionFailedf ("could not read L0 sublevels" )
910- }
911- bounds := meta .UserKeyBounds ()
912-
913- // Check for overlap over the keys of L0.
914- if overlap , err := overlapChecker .DetermineAnyDataOverlapInLevel (ctx , bounds , 0 ); err != nil {
915- return 0 , nil , err
916- } else if overlap {
907+ if lsmOverlap [0 ].result == dataOverlap {
917908 return 0 , nil , nil
918909 }
919-
910+ targetLevel = 0
911+ splitFile = nil
920912 for level := baseLevel ; level < numLevels ; level ++ {
921- dataOverlap , err := overlapChecker .DetermineAnyDataOverlapInLevel (ctx , bounds , level )
922- if err != nil {
923- return 0 , nil , err
924- } else if dataOverlap {
913+ var candidateSplitFile * fileMetadata
914+ switch lsmOverlap [level ].result {
915+ case dataOverlap :
916+ // We cannot ingest into or under this level; return the best target level
917+ // so far.
925918 return targetLevel , splitFile , nil
926- }
927919
928- // Check boundary overlap.
929- var candidateSplitFile * fileMetadata
930- boundaryOverlaps := overlapChecker .v .Overlaps (level , bounds )
931- if ! boundaryOverlaps .Empty () {
932- // We are already guaranteed to not have any data overlaps with files
933- // in boundaryOverlaps, otherwise we'd have returned in the above if
934- // statements. Use this, plus boundaryOverlaps.Len() == 1 to detect for
935- // the case where we can slot this file into the current level despite
936- // a boundary overlap, by splitting one existing file into two virtual
937- // sstables.
938- if suggestSplit && boundaryOverlaps .Len () == 1 {
939- iter := boundaryOverlaps .Iter ()
940- candidateSplitFile = iter .First ()
941- } else {
942- // We either don't want to suggest ingest-time splits (i.e.
943- // !suggestSplit), or we boundary-overlapped with more than one file.
920+ case noDataOverlap :
921+ if ! suggestSplit || lsmOverlap [level ].splitFile == nil {
922+ // We can ingest under this level, but not into this level.
944923 continue
945924 }
925+ // We can ingest into this level if we split this file.
926+ candidateSplitFile = lsmOverlap [level ].splitFile
927+
928+ case noBoundaryOverlap :
929+ // We can ingest into this level.
930+
931+ default :
932+ return 0 , nil , base .AssertionFailedf ("unexpected lsmOverlap result: %v" , lsmOverlap [level ].result )
946933 }
947934
948935 // Check boundary overlap with any ongoing compactions. We consider an
@@ -964,8 +951,8 @@ func ingestTargetLevel(
964951 if c .outputLevel == nil || level != c .outputLevel .level {
965952 continue
966953 }
967- if overlapChecker . comparer . Compare (meta .Smallest .UserKey , c .largest .UserKey ) <= 0 &&
968- overlapChecker . comparer . Compare (meta .Largest .UserKey , c .smallest .UserKey ) >= 0 {
954+ if cmp (meta .Smallest .UserKey , c .largest .UserKey ) <= 0 &&
955+ cmp (meta .Largest .UserKey , c .smallest .UserKey ) >= 0 {
969956 overlaps = true
970957 break
971958 }
@@ -2202,14 +2189,20 @@ func (d *DB) ingestApply(
22022189 f .Level = 6
22032190 }
22042191 } else {
2205- // TODO(bilal): ingestTargetLevel does disk IO (reading files for data
2206- // overlap) even though we're holding onto d.mu. Consider unlocking
2207- // d.mu while we do this. We already hold versions.logLock so we should
2208- // not see any version applications while we're at this. The one
2209- // complication here would be pulling out the mu.compact.inProgress
2210- // check from ingestTargetLevel, as that requires d.mu to be held.
2211- f .Level , splitFile , err = ingestTargetLevel (ctx , overlapChecker ,
2212- baseLevel , d .mu .compact .inProgress , m , shouldIngestSplit )
2192+ // We check overlap against the LSM without holding DB.mu. Note that we
2193+ // are still holding the log lock, so the version cannot change.
2194+ // TODO(radu): perform this check optimistically outside of the log lock.
2195+ var overlap lsmOverlap
2196+ overlap , err = func () (lsmOverlap , error ) {
2197+ d .mu .Unlock ()
2198+ defer d .mu .Lock ()
2199+ return overlapChecker .DetermineLSMOverlap (ctx , m .UserKeyBounds ())
2200+ }()
2201+ if err == nil {
2202+ f .Level , splitFile , err = ingestTargetLevel (
2203+ ctx , d .cmp , overlap , baseLevel , d .mu .compact .inProgress , m , shouldIngestSplit ,
2204+ )
2205+ }
22132206 }
22142207
22152208 if splitFile != nil {
0 commit comments