LCOV - code coverage report
Current view: top level - pebble/internal/keyspan/keyspanimpl - level_iter.go (source / functions) Hit Total Coverage
Test: 2024-08-15 08:17Z 25ac6781 - tests only.lcov Lines: 238 270 88.1 %
Date: 2024-08-15 08:17:37 Functions: 0 0 -

          Line data    Source code
       1             : // Copyright 2022 The LevelDB-Go and Pebble Authors. All rights reserved. Use
       2             : // of this source code is governed by a BSD-style license that can be found in
       3             : // the LICENSE file.
       4             : 
       5             : package keyspanimpl
       6             : 
       7             : import (
       8             :         "context"
       9             :         "fmt"
      10             : 
      11             :         "github.com/cockroachdb/pebble/internal/base"
      12             :         "github.com/cockroachdb/pebble/internal/invariants"
      13             :         "github.com/cockroachdb/pebble/internal/keyspan"
      14             :         "github.com/cockroachdb/pebble/internal/manifest"
      15             :         "github.com/cockroachdb/pebble/internal/treeprinter"
      16             : )
      17             : 
      18             : // TableNewSpanIter creates a new iterator for range key spans for the given
      19             : // file.
      20             : type TableNewSpanIter func(
      21             :         ctx context.Context, file *manifest.FileMetadata, iterOptions keyspan.SpanIterOptions,
      22             : ) (keyspan.FragmentIterator, error)
      23             : 
      24             : // LevelIter provides a merged view of spans from sstables in an L1+ level or an
      25             : // L0 sublevel.
      26             : //
      27             : // LevelIter takes advantage of level invariants to only have one sstable span
      28             : // block open at one time, opened using the newIter function passed in.
      29             : //
      30             : // A LevelIter is configured with a key type that is either KeyTypePoint
      31             : // (corresponding to range dels) or KeyTypeRange (corresponding to range keys).
      32             : // The key type decides which bounds we use for the files (and which files we
      33             : // filter out).
      34             : //
      35             : // LevelIter supports emitting "straddling spans": these are empty spans that
      36             : // cover the gaps between the keyspaces of adjacent files. This is an
      37             : // optimization to avoid unnecessarily loading files in cases where spans are
      38             : // very sparse (in the context of merging spans from multiple levels). We
      39             : // currently produce straddling spans only in range key mode.
      40             : //
      41             : // TODO(radu): investigate enabling straddling spans for rangedel mode.
      42             : type LevelIter struct {
      43             :         cmp     base.Compare
      44             :         keyType manifest.KeyType
      45             :         // The LSM level this LevelIter is initialized for.
      46             :         level manifest.Layer
      47             :         // newIter creates a range del iterator if keyType is KeyTypePoint or a range
      48             :         // key iterator if keyType is KeyTypeRange.
      49             :         newIter TableNewSpanIter
      50             :         // ctx is passed to TableNewSpanIter.
      51             :         ctx context.Context
      52             : 
      53             :         // The options that were passed in.
      54             :         tableOpts keyspan.SpanIterOptions
      55             : 
      56             :         files manifest.LevelIterator
      57             : 
      58             :         // file always corresponds to the current position of LevelIter.files.
      59             :         file *manifest.FileMetadata
      60             :         pos  levelIterPos
      61             :         // fileIter is the iterator for LevelIter.file when pos is atFile; it is nil
      62             :         // otherwise.
      63             :         fileIter keyspan.FragmentIterator
      64             : 
      65             :         // lastIter retains the last opened iterator, in case that the next time we
      66             :         // need an iterator it is for the same file. When fileIter is not nil,
      67             :         // fileIter is the same with lastIter and file is the same with lastIterFile.
      68             :         lastIter     keyspan.FragmentIterator
      69             :         lastIterFile *manifest.FileMetadata
      70             : 
      71             :         wrapFn       keyspan.WrapFn
      72             :         straddleSpan keyspan.Span
      73             : 
      74             :         // TODO(bilal): Add InternalIteratorStats.
      75             : }
      76             : 
      77             : // LevelIter implements the keyspan.FragmentIterator interface.
      78             : var _ keyspan.FragmentIterator = (*LevelIter)(nil)
      79             : 
      80             : // NewLevelIter returns a LevelIter.
      81             : //
      82             : // newIter must create a range del iterator for the given file if keyType is
      83             : // KeyTypePoint or a range key iterator if keyType is KeyTypeRange.
      84             : func NewLevelIter(
      85             :         ctx context.Context,
      86             :         opts keyspan.SpanIterOptions,
      87             :         cmp base.Compare,
      88             :         newIter TableNewSpanIter,
      89             :         files manifest.LevelIterator,
      90             :         level manifest.Layer,
      91             :         keyType manifest.KeyType,
      92           1 : ) *LevelIter {
      93           1 :         l := &LevelIter{}
      94           1 :         l.Init(ctx, opts, cmp, newIter, files, level, keyType)
      95           1 :         return l
      96           1 : }
      97             : 
      98             : // Init initializes a LevelIter.
      99             : //
     100             : // newIter must create a range del iterator for the given file if keyType is
     101             : // KeyTypePoint or a range key iterator if keyType is KeyTypeRange.
     102             : func (l *LevelIter) Init(
     103             :         ctx context.Context,
     104             :         opts keyspan.SpanIterOptions,
     105             :         cmp base.Compare,
     106             :         newIter TableNewSpanIter,
     107             :         files manifest.LevelIterator,
     108             :         level manifest.Layer,
     109             :         keyType manifest.KeyType,
     110           1 : ) {
     111           1 :         if keyType != manifest.KeyTypePoint && keyType != manifest.KeyTypeRange {
     112           0 :                 panic("keyType must be point or range")
     113             :         }
     114           1 :         *l = LevelIter{
     115           1 :                 cmp:       cmp,
     116           1 :                 keyType:   keyType,
     117           1 :                 level:     level,
     118           1 :                 newIter:   newIter,
     119           1 :                 ctx:       ctx,
     120           1 :                 tableOpts: opts,
     121           1 :                 files:     files.Filter(keyType),
     122           1 :         }
     123           1 :         l.setPosAfterFile(nil)
     124             : }
     125             : 
     126             : // levelIterPos narrows down the position of the iterator in relation to the file:
     127             : //
     128             : //   - atFile: the iterator is currently positioned inside LevelIter.file.
     129             : //
     130             : //   - beforeFile: the iterator is currently positioned right before
     131             : //     LevelIter.file. If .file is not the first file, this position corresponds
     132             : //     to a straddle span.
     133             : //
     134             : //   - afterFile: the iterator is currently positioned right after
     135             : //     LevelIter.file. If .file is not the last file, this position corresponds
     136             : //     to a straddle span.
     137             : //
     138             : // Example:
     139             : //
     140             : //                    beforeFile    atFile   afterFile
     141             : //                             |      |      |
     142             : //                             v      v      v
     143             : //      ..--- .files.Prev() ------- .file ------- .files.Next() ---...
     144             : //
     145             : // Note that each straddle position can be represented in two different ways
     146             : // (either after one file, or before the other file). We use the one which makes
     147             : // it easier to keep l.file in sync with the l.files iterator (which depends on
     148             : // the iteration direction).
     149             : //
     150             : // When file is nil, it should be considered a sentinel either before or after
     151             : // all the files.  When file is nil and pos is afterFile, we are positioned
     152             : // after the imaginary start sentinel, i.e. before the first file:
     153             : //
     154             : //                       afterFile
     155             : //                            |
     156             : //                            v
     157             : //              .file=nil ------- .files.First() ---...
     158             : //
     159             : //       When file is nil and pos is beforeFile, we are positioned after the
     160             : //       imaginary end sentinel, i.e. after the last file:
     161             : //
     162             : //
     163             : //                           beforeFile
     164             : //                               |
     165             : //                               v
     166             : //              ...--- .files.Last() ------- .file=nil
     167             : //
     168             : // Note that when straddle spans are not emitted, the position is always
     169             : // `atFile` unless the iterator is exhausted.
     170             : type levelIterPos uint8
     171             : 
     172             : const (
     173             :         atFile levelIterPos = iota
     174             :         beforeFile
     175             :         afterFile
     176             : )
     177             : 
     178             : // SeekGE implements keyspan.FragmentIterator.
     179           1 : func (l *LevelIter) SeekGE(key []byte) (*keyspan.Span, error) {
     180           1 :         file := l.files.SeekGE(l.cmp, key)
     181           1 :         if file == nil {
     182           1 :                 l.setPosBeforeFile(nil)
     183           1 :                 return nil, nil
     184           1 :         }
     185           1 :         if l.straddleSpansEnabled() && l.cmp(key, file.SmallestRangeKey.UserKey) < 0 {
     186           1 :                 // Peek at the previous file.
     187           1 :                 if prevFile := l.files.Prev(); prevFile != nil {
     188           1 :                         // We could unconditionally return an empty span between the seek key and
     189           1 :                         // f.SmallestRangeKey, however if this span is to the left of all range
     190           1 :                         // keys on this level, it could lead to inconsistent behaviour in relative
     191           1 :                         // positioning operations. Consider this example, with a b-c range key:
     192           1 :                         //   SeekGE(a) -> a-b:{}
     193           1 :                         //   Next() -> b-c{(#5,RANGEKEYSET,@4,foo)}
     194           1 :                         //   Prev() -> nil
     195           1 :                         // Iterators higher up in the iterator stack rely on this sort
     196           1 :                         // of relative positioning consistency.
     197           1 :                         //
     198           1 :                         // TODO(bilal): Investigate ways to be able to return straddle spans in
     199           1 :                         // cases similar to the above, while still retaining correctness.
     200           1 :                         // Return a straddling key instead of loading the file.
     201           1 :                         l.setPosAfterFile(prevFile)
     202           1 :                         return l.makeStraddleSpan(prevFile, file), nil
     203           1 :                 }
     204             :                 // Return the iterator to file.
     205           1 :                 l.files.Next()
     206             :         }
     207             : 
     208           1 :         if err := l.setPosAtFile(file); err != nil {
     209           1 :                 return nil, err
     210           1 :         }
     211           1 :         if span, err := l.fileIter.SeekGE(key); span != nil || err != nil {
     212           1 :                 return span, err
     213           1 :         }
     214           1 :         return l.moveToNextFile()
     215             : }
     216             : 
     217             : // SeekLT implements keyspan.FragmentIterator.
     218           1 : func (l *LevelIter) SeekLT(key []byte) (*keyspan.Span, error) {
     219           1 :         file := l.files.SeekLT(l.cmp, key)
     220           1 :         if file == nil {
     221           1 :                 l.setPosAfterFile(nil)
     222           1 :                 return nil, nil
     223           1 :         }
     224           1 :         if l.straddleSpansEnabled() && l.cmp(file.LargestRangeKey.UserKey, key) < 0 {
     225           1 :                 // Peek at the next file.
     226           1 :                 if nextFile := l.files.Next(); nextFile != nil {
     227           1 :                         // We could unconditionally return an empty span between f.LargestRangeKey
     228           1 :                         // and the seek key, however if this span is to the right of all range keys
     229           1 :                         // on this level, it could lead to inconsistent behaviour in relative
     230           1 :                         // positioning operations. Consider this example, with a b-c range key:
     231           1 :                         //   SeekLT(d) -> c-d:{}
     232           1 :                         //   Prev() -> b-c{(#5,RANGEKEYSET,@4,foo)}
     233           1 :                         //   Next() -> nil
     234           1 :                         // Iterators higher up in the iterator stack rely on this sort of relative
     235           1 :                         // positioning consistency.
     236           1 :                         //
     237           1 :                         // TODO(bilal): Investigate ways to be able to return straddle spans in
     238           1 :                         // cases similar to the above, while still retaining correctness.
     239           1 :                         // Return a straddling key instead of loading the file.
     240           1 :                         l.setPosBeforeFile(nextFile)
     241           1 :                         return l.makeStraddleSpan(file, nextFile), nil
     242           1 :                 }
     243             :                 // Return the iterator to file.
     244           1 :                 l.files.Prev()
     245             :         }
     246           1 :         if err := l.setPosAtFile(file); err != nil {
     247           1 :                 return nil, err
     248           1 :         }
     249           1 :         if span, err := l.fileIter.SeekLT(key); span != nil || err != nil {
     250           1 :                 return span, err
     251           1 :         }
     252           1 :         return l.moveToPrevFile()
     253             : }
     254             : 
     255             : // First implements keyspan.FragmentIterator.
     256           1 : func (l *LevelIter) First() (*keyspan.Span, error) {
     257           1 :         file := l.files.First()
     258           1 :         if file == nil {
     259           1 :                 l.setPosBeforeFile(nil)
     260           1 :                 return nil, nil
     261           1 :         }
     262           1 :         if err := l.setPosAtFile(file); err != nil {
     263           1 :                 return nil, err
     264           1 :         }
     265           1 :         if span, err := l.fileIter.First(); span != nil || err != nil {
     266           1 :                 return span, err
     267           1 :         }
     268           1 :         return l.moveToNextFile()
     269             : }
     270             : 
     271             : // Last implements keyspan.FragmentIterator.
     272           1 : func (l *LevelIter) Last() (*keyspan.Span, error) {
     273           1 :         file := l.files.Last()
     274           1 :         if file == nil {
     275           0 :                 l.setPosAfterFile(nil)
     276           0 :                 return nil, nil
     277           0 :         }
     278           1 :         if err := l.setPosAtFile(file); err != nil {
     279           1 :                 return nil, err
     280           1 :         }
     281           1 :         if span, err := l.fileIter.Last(); span != nil || err != nil {
     282           1 :                 return span, err
     283           1 :         }
     284           1 :         return l.moveToPrevFile()
     285             : }
     286             : 
     287             : // Next implements keyspan.FragmentIterator.
     288           1 : func (l *LevelIter) Next() (*keyspan.Span, error) {
     289           1 :         if l.file == nil {
     290           1 :                 if l.pos == afterFile {
     291           1 :                         return l.First()
     292           1 :                 }
     293             :                 // Iterator is exhausted.
     294           1 :                 return nil, nil
     295             :         }
     296           1 :         switch l.pos {
     297           1 :         case atFile:
     298           1 :                 if span, err := l.fileIter.Next(); span != nil || err != nil {
     299           1 :                         return span, err
     300           1 :                 }
     301           1 :         case beforeFile:
     302           1 :                 // We were positioned on a straddle span before l.file; now we can advance to the file.
     303           1 :                 if err := l.setPosAtFile(l.file); err != nil {
     304           0 :                         return nil, err
     305           0 :                 }
     306           1 :                 if span, err := l.fileIter.First(); span != nil || err != nil {
     307           1 :                         return span, err
     308           1 :                 }
     309           1 :         case afterFile:
     310             :                 // We were positioned on a straddle span after l.file. Move to the next file.
     311             :         }
     312           1 :         return l.moveToNextFile()
     313             : }
     314             : 
     315             : // Prev implements keyspan.FragmentIterator.
     316           1 : func (l *LevelIter) Prev() (*keyspan.Span, error) {
     317           1 :         if l.file == nil {
     318           1 :                 if l.pos == beforeFile {
     319           1 :                         return l.Last()
     320           1 :                 }
     321             :                 // Iterator is exhausted.
     322           1 :                 return nil, nil
     323             :         }
     324           1 :         switch l.pos {
     325           1 :         case atFile:
     326           1 :                 if span, err := l.fileIter.Prev(); span != nil || err != nil {
     327           1 :                         return span, err
     328           1 :                 }
     329           1 :         case afterFile:
     330           1 :                 // We were positioned on a straddle span after l.file; now we can advance
     331           1 :                 // (backwards) to the file.
     332           1 :                 if err := l.setPosAtFile(l.file); err != nil {
     333           0 :                         return nil, err
     334           0 :                 }
     335           1 :                 if span, err := l.fileIter.Last(); span != nil || err != nil {
     336           1 :                         return span, err
     337           1 :                 }
     338           1 :         case beforeFile:
     339             :                 // We were positioned on a straddle span before l.file. Move to the previous file.
     340             :         }
     341           1 :         return l.moveToPrevFile()
     342             : }
     343             : 
     344           1 : func (l *LevelIter) moveToNextFile() (*keyspan.Span, error) {
     345           1 :         if invariants.Enabled && l.pos == beforeFile {
     346           0 :                 panic("moveToNextFile with beforeFile pos")
     347             :         }
     348           1 :         for {
     349           1 :                 nextFile := l.files.Next()
     350           1 :                 if nextFile == nil {
     351           1 :                         l.setPosBeforeFile(nil)
     352           1 :                         return nil, nil
     353           1 :                 }
     354             :                 // Emit a straddle span, if necessary.
     355           1 :                 if l.pos == atFile && nextFile != nil && l.needStraddleSpan(l.file, nextFile) {
     356           1 :                         span := l.makeStraddleSpan(l.file, nextFile)
     357           1 :                         l.setPosBeforeFile(nextFile)
     358           1 :                         return span, nil
     359           1 :                 }
     360           1 :                 if err := l.setPosAtFile(nextFile); err != nil {
     361           0 :                         return nil, err
     362           0 :                 }
     363           1 :                 if span, err := l.fileIter.First(); span != nil || err != nil {
     364           1 :                         return span, err
     365           1 :                 }
     366             :                 // The file had no spans; continue.
     367             :         }
     368             : }
     369             : 
     370           1 : func (l *LevelIter) moveToPrevFile() (*keyspan.Span, error) {
     371           1 :         if invariants.Enabled && l.pos == afterFile {
     372           0 :                 panic("eofBackward with afterFile pos")
     373             :         }
     374           1 :         for {
     375           1 :                 prevFile := l.files.Prev()
     376           1 :                 if prevFile == nil {
     377           1 :                         l.setPosAfterFile(nil)
     378           1 :                         return nil, nil
     379           1 :                 }
     380             :                 // Emit a straddle span, if necessary.
     381           1 :                 if l.pos == atFile && l.file != nil && l.needStraddleSpan(prevFile, l.file) {
     382           1 :                         span := l.makeStraddleSpan(prevFile, l.file)
     383           1 :                         l.setPosAfterFile(prevFile)
     384           1 :                         return span, nil
     385           1 :                 }
     386           1 :                 if err := l.setPosAtFile(prevFile); err != nil {
     387           0 :                         return nil, err
     388           0 :                 }
     389           1 :                 if span, err := l.fileIter.Last(); span != nil || err != nil {
     390           1 :                         return span, err
     391           1 :                 }
     392             :                 // The file had no spans; continue.
     393             :         }
     394             : }
     395             : 
     396             : // SetContext is part of the FragmentIterator interface.
     397           0 : func (l *LevelIter) SetContext(ctx context.Context) {
     398           0 :         l.ctx = ctx
     399           0 :         if l.lastIter != nil {
     400           0 :                 l.lastIter.SetContext(ctx)
     401           0 :         }
     402             : }
     403             : 
     404             : // Close implements keyspan.FragmentIterator.
     405           1 : func (l *LevelIter) Close() {
     406           1 :         l.file = nil
     407           1 :         l.fileIter = nil
     408           1 :         if l.lastIter != nil {
     409           1 :                 l.lastIter.Close()
     410           1 :                 l.lastIter = nil
     411           1 :                 l.lastIterFile = nil
     412           1 :         }
     413             : }
     414             : 
     415             : // String implements keyspan.FragmentIterator.
     416           1 : func (l *LevelIter) String() string {
     417           1 :         if l.file != nil {
     418           1 :                 return fmt.Sprintf("%s: fileNum=%s", l.level, l.file.FileNum)
     419           1 :         }
     420           0 :         return fmt.Sprintf("%s: fileNum=<nil>", l.level)
     421             : }
     422             : 
     423             : // WrapChildren implements FragmentIterator.
     424           0 : func (l *LevelIter) WrapChildren(wrap keyspan.WrapFn) {
     425           0 :         if l.fileIter != nil {
     426           0 :                 l.fileIter = wrap(l.fileIter)
     427           0 :         }
     428           0 :         l.wrapFn = wrap
     429             : }
     430             : 
     431             : // DebugTree is part of the FragmentIterator interface.
     432           0 : func (l *LevelIter) DebugTree(tp treeprinter.Node) {
     433           0 :         n := tp.Childf("%T(%p) %s", l, l, l.level)
     434           0 :         if l.fileIter != nil {
     435           0 :                 l.fileIter.DebugTree(n)
     436           0 :         }
     437             : }
     438             : 
     439           1 : func (l *LevelIter) setPosBeforeFile(f *manifest.FileMetadata) {
     440           1 :         l.setPosInternal(f, beforeFile)
     441           1 : }
     442             : 
     443           1 : func (l *LevelIter) setPosAfterFile(f *manifest.FileMetadata) {
     444           1 :         l.setPosInternal(f, afterFile)
     445           1 : }
     446             : 
     447             : // setPosAtFile sets the current position and opens an iterator for the file (if
     448             : // necessary).
     449           1 : func (l *LevelIter) setPosAtFile(f *manifest.FileMetadata) error {
     450           1 :         l.setPosInternal(f, atFile)
     451           1 :         // See if the last iterator was for the same file; if not, close it and open a
     452           1 :         // new one.
     453           1 :         if l.lastIter == nil || l.lastIterFile != f {
     454           1 :                 if l.lastIter != nil {
     455           1 :                         l.lastIter.Close()
     456           1 :                         l.lastIter = nil
     457           1 :                         l.lastIterFile = nil
     458           1 :                 }
     459           1 :                 iter, err := l.newIter(l.ctx, l.file, l.tableOpts)
     460           1 :                 if err != nil {
     461           1 :                         return err
     462           1 :                 }
     463           1 :                 iter = keyspan.MaybeAssert(iter, l.cmp)
     464           1 :                 if l.wrapFn != nil {
     465           0 :                         iter = l.wrapFn(iter)
     466           0 :                 }
     467           1 :                 l.lastIter = iter
     468           1 :                 l.lastIterFile = f
     469             :         }
     470           1 :         l.fileIter = l.lastIter
     471           1 :         return nil
     472             : }
     473             : 
     474             : // setPos sets l.file and l.pos (and closes the iteris for the new file).
     475           1 : func (l *LevelIter) setPosInternal(f *manifest.FileMetadata, pos levelIterPos) {
     476           1 :         l.file = f
     477           1 :         l.fileIter = nil
     478           1 :         l.pos = pos
     479           1 : }
     480             : 
     481           1 : func (l *LevelIter) straddleSpansEnabled() bool {
     482           1 :         return l.keyType == manifest.KeyTypeRange
     483           1 : }
     484             : 
     485             : // needStraddleSpan returns true if straddle spans are enabled and there is a
     486             : // gap between the bounds of the files. file and nextFile are assumed to be
     487             : // consecutive files in the level, in the order they appear in the level.
     488           1 : func (l *LevelIter) needStraddleSpan(file, nextFile *manifest.FileMetadata) bool {
     489           1 :         // We directly use range key bounds because that is the current condition for
     490           1 :         // straddleSpansEnabled.
     491           1 :         return l.straddleSpansEnabled() && l.cmp(file.LargestRangeKey.UserKey, nextFile.SmallestRangeKey.UserKey) < 0
     492           1 : }
     493             : 
     494             : // makeStraddleSpan returns a straddle span that covers the gap between file and
     495             : // nextFile.
     496           1 : func (l *LevelIter) makeStraddleSpan(file, nextFile *manifest.FileMetadata) *keyspan.Span {
     497           1 :         l.straddleSpan = keyspan.Span{
     498           1 :                 Start: file.LargestRangeKey.UserKey,
     499           1 :                 End:   nextFile.SmallestRangeKey.UserKey,
     500           1 :                 Keys:  nil,
     501           1 :         }
     502           1 :         return &l.straddleSpan
     503           1 : }

Generated by: LCOV version 1.14