@@ -18,10 +18,6 @@ import (
1818 "github.com/cockroachdb/pebble/internal/invariants"
1919)
2020
21- // errInvalidL0SublevelsOpt is for use in addL0Files when the incremental
22- // sublevel generation optimization failed, and newL0Sublevels must be called.
23- var errInvalidL0SublevelsOpt = errors .New ("pebble: L0 sublevel generation optimization cannot be used" )
24-
2521// Intervals are of the form [start, end) with no gap between intervals. Each
2622// file overlaps perfectly with a sequence of intervals. This perfect overlap
2723// occurs because the union of file boundary keys is used to pick intervals.
@@ -314,9 +310,7 @@ func newL0Sublevels(
314310 // Initialize minIntervalIndex and maxIntervalIndex for each file, and use that
315311 // to update intervals.
316312 for f := iter .First (); f != nil ; f = iter .Next () {
317- if err := s .addFileToSublevels (f , false /* checkInvariant */ ); err != nil {
318- return nil , err
319- }
313+ s .addFileToSublevels (f )
320314 }
321315 // Sort each sublevel in increasing key order.
322316 for i := range s .levelFiles {
@@ -409,51 +403,20 @@ func mergeIntervals(
409403 return result , oldToNewMap
410404}
411405
412- // addL0Files incrementally builds a new l0Sublevels for when the only change
413- // since the receiver l0Sublevels was an addition of the specified tables, with
414- // no L0 deletions. The common case of this is an ingestion or a flush. These
415- // files can "sit on top" of existing sublevels, creating at most one new
416- // sublevel for a flush (and possibly multiple for an ingestion), and at most
417- // 2*len(files) additions to s.orderedIntervals. No files must have been deleted
418- // from L0, and the added files must all be newer in sequence numbers than
419- // existing files in l0Sublevels. The levelMetadata parameter corresponds to the
420- // new L0 post addition of files. This method is meant to be significantly more
421- // performant than newL0Sublevels.
422- //
423- // Note that this function can only be called once on a given receiver; it
424- // appends to some slices in s which is only safe when done once. This is okay,
425- // as the common case (generating a new l0Sublevels after a flush/ingestion) is
426- // only going to necessitate one call of this method on a given receiver. The
427- // returned value, if non-nil, can then have [*l0Sublevels.addL0Files] called on
428- // it again, and so on. If [errInvalidL0SublevelsOpt] is returned as an error,
429- // it means the optimization could not be applied (i.e. files added were older
430- // than files already in the sublevels, which is possible around ingestions and
431- // in tests). Eg. it can happen when an ingested file was ingested without
432- // queueing a flush since it did not actually overlap with any keys in the
433- // memtable. Later on the memtable was flushed, and the memtable had keys
434- // spanning around the ingested file, producing a flushed file that overlapped
435- // with the ingested file in file bounds but not in keys. It's possible for that
436- // flushed file to have a lower LargestSeqNum than the ingested file if all the
437- // additions after the ingestion were to another flushed file that was split
438- // into a separate sstable during flush. Any other non-nil error means
439- // [l0Sublevels] generation failed in the same way as [newL0Sublevels] would
440- // likely fail.
441- func (s * l0Sublevels ) addL0Files (
442- addedTables map [base.FileNum ]* TableMetadata ,
443- flushSplitMaxBytes int64 ,
444- levelMetadata * LevelMetadata ,
445- ) (* l0Sublevels , error ) {
406+ func (s * l0Sublevels ) canUseAddL0Files (
407+ addedTables map [base.FileNum ]* TableMetadata , levelMetadata * LevelMetadata ,
408+ ) (filesToAddInOrder []* TableMetadata , ok bool ) {
446409 if s .addL0FilesCalled {
447410 if invariants .Enabled {
448411 panic ("addL0Files called twice on the same receiver" )
449412 }
450- return nil , errInvalidL0SublevelsOpt
413+ return nil , false
451414 }
452415 if s .levelMetadata .Len ()+ len (addedTables ) != levelMetadata .Len () {
453416 if invariants .Enabled {
454417 panic ("levelMetadata mismatch" )
455418 }
456- return nil , errInvalidL0SublevelsOpt
419+ return nil , false
457420 }
458421
459422 // addL0Files only works when the files we are adding match exactly the last
@@ -465,12 +428,40 @@ func (s *l0Sublevels) addL0Files(
465428 if addedTables [t .FileNum ] == nil {
466429 // t is an existing table that sorts after some of the new tables
467430 // (specifically the ones we haven't yet seen).
468- return nil , errInvalidL0SublevelsOpt
431+ return nil , false
469432 }
470433 files [i ] = t
471434 t = iter .Prev ()
472435 }
473- // Note: files now contains addedTables in increasing seqnum order.
436+ return files , true
437+ }
438+
439+ // addL0Files incrementally builds a new l0Sublevels for when the only change
440+ // since the receiver l0Sublevels was an addition of the specified tables, with
441+ // no L0 deletions. The common case of this is an ingestion or a flush. These
442+ // files can "sit on top" of existing sublevels, creating at most one new
443+ // sublevel for a flush (and possibly multiple for an ingestion), and at most
444+ // 2*len(files) additions to s.orderedIntervals. No files must have been deleted
445+ // from L0, and the added files must all be newer in sequence numbers than
446+ // existing files in l0Sublevels. The levelMetadata parameter corresponds to the
447+ // new L0 post addition of files. This method is meant to be significantly more
448+ // performant than newL0Sublevels.
449+ //
450+ // This function is intended to be called with the result of canUseAddL0Files(),
451+ // which is the list of new L0 tables in increasing L0 order.
452+ //
453+ // Note that this function can only be called once on a given receiver; it
454+ // appends to some slices in s which is only safe when done once. This is okay,
455+ // as the common case (generating a new l0Sublevels after a flush/ingestion) is
456+ // only going to necessitate one call of this method on a given receiver. The
457+ // returned value, if non-nil, can then have [*l0Sublevels.addL0Files] called on
458+ // it again, and so on.
459+ func (s * l0Sublevels ) addL0Files (
460+ files []* TableMetadata , flushSplitMaxBytes int64 , levelMetadata * LevelMetadata ,
461+ ) * l0Sublevels {
462+ if s .addL0FilesCalled {
463+ panic ("addL0Files called twice on the same receiver" )
464+ }
474465 s .addL0FilesCalled = true
475466
476467 // Start with a shallow copy of s.
@@ -618,9 +609,7 @@ func (s *l0Sublevels) addL0Files(
618609 // Update interval indices for new files.
619610 for i , f := range files {
620611 f .L0Index = s .levelMetadata .Len () + i
621- if err := newVal .addFileToSublevels (f , true /* checkInvariant */ ); err != nil {
622- return nil , err
623- }
612+ newVal .addFileToSublevels (f )
624613 updatedSublevels = append (updatedSublevels , f .SubLevel )
625614 }
626615
@@ -658,16 +647,14 @@ func (s *l0Sublevels) addL0Files(
658647 newVal .flushSplitUserKeys = nil
659648 newVal .calculateFlushSplitKeys (flushSplitMaxBytes )
660649 newVal .Check ()
661- return newVal , nil
650+ return newVal
662651}
663652
664653// addFileToSublevels is called during l0Sublevels generation, and adds f to the
665654// correct sublevel's levelFiles, the relevant intervals' files slices, and sets
666655// interval indices on f. This method, if called successively on multiple files,
667- // _must_ be called on successively newer files (by seqnum). If checkInvariant
668- // is true, it could check for this in some cases and return
669- // [errInvalidL0SublevelsOpt] if that invariant isn't held.
670- func (s * l0Sublevels ) addFileToSublevels (f * TableMetadata , checkInvariant bool ) error {
656+ // _must_ be called on successively newer files (by seqnum).
657+ func (s * l0Sublevels ) addFileToSublevels (f * TableMetadata ) {
671658 // This is a simple and not very accurate estimate of the number of
672659 // bytes this SSTable contributes to the intervals it is a part of.
673660 //
@@ -680,10 +667,8 @@ func (s *l0Sublevels) addFileToSublevels(f *TableMetadata, checkInvariant bool)
680667 for i := f .minIntervalIndex ; i <= f .maxIntervalIndex ; i ++ {
681668 interval := & s .orderedIntervals [i ]
682669 if len (interval .files ) > 0 {
683- if checkInvariant && interval .files [len (interval .files )- 1 ].LargestSeqNum > f .LargestSeqNum {
684- // We are sliding this file "underneath" an existing file. Throw away
685- // and start over in newL0Sublevels.
686- return errInvalidL0SublevelsOpt
670+ if interval .files [len (interval .files )- 1 ].LargestSeqNum > f .LargestSeqNum {
671+ panic (errors .AssertionFailedf ("addFileToSublevels found existing newer file" ))
687672 }
688673 // interval.files is sorted by sublevels, from lowest to highest.
689674 // addL0Files can only add files at sublevels higher than existing files
@@ -703,14 +688,13 @@ func (s *l0Sublevels) addFileToSublevels(f *TableMetadata, checkInvariant bool)
703688 }
704689 f .SubLevel = subLevel
705690 if subLevel > len (s .levelFiles ) {
706- return errors .Errorf ("chose a sublevel beyond allowed range of sublevels: %d vs 0-%d" , subLevel , len (s .levelFiles ))
691+ panic ( errors .AssertionFailedf ("chose a sublevel beyond allowed range of sublevels: %d vs 0-%d" , subLevel , len (s .levelFiles ) ))
707692 }
708693 if subLevel == len (s .levelFiles ) {
709694 s .levelFiles = append (s .levelFiles , []* TableMetadata {f })
710695 } else {
711696 s .levelFiles [subLevel ] = append (s .levelFiles [subLevel ], f )
712697 }
713- return nil
714698}
715699
716700func (s * l0Sublevels ) calculateFlushSplitKeys (flushSplitMaxBytes int64 ) {
@@ -2147,8 +2131,8 @@ func (o *L0Organizer) Update(
21472131 }
21482132 // If we only added tables, try to use addL0Files.
21492133 if len (deletedL0Tables ) == 0 {
2150- newSublevels , err := o .l0Sublevels .addL0Files (addedL0Tables , o . flushSplitBytes , newLevelMeta )
2151- if err == nil {
2134+ if files , ok := o .l0Sublevels .canUseAddL0Files (addedL0Tables , newLevelMeta ); ok {
2135+ newSublevels := o . l0Sublevels . addL0Files ( files , o . flushSplitBytes , newLevelMeta )
21522136 // In invariants mode, sometimes rebuild from scratch to verify that
21532137 // AddL0Files did the right thing. Note that NewL0Sublevels updates
21542138 // fields in TableMetadata like L0Index, so we don't want to do this
@@ -2170,9 +2154,6 @@ func (o *L0Organizer) Update(
21702154 o .l0Sublevels = newSublevels
21712155 return
21722156 }
2173- if ! errors .Is (err , errInvalidL0SublevelsOpt ) {
2174- panic (errors .AssertionFailedf ("error generating L0Sublevels: %s" , err ))
2175- }
21762157 }
21772158 var err error
21782159 o .l0Sublevels , err = newL0Sublevels (newLevelMeta , o .cmp , o .formatKey , o .flushSplitBytes )
0 commit comments