LCOV - code coverage report
Current view: top level - pebble - scan_internal.go (source / functions) Hit Total Coverage
Test: 2024-07-21 08:15Z 72c3f550 - tests + meta.lcov Lines: 616 729 84.5 %
Date: 2024-07-21 08:16:56 Functions: 0 0 -

          Line data    Source code
       1             : // Copyright 2023 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 pebble
       6             : 
       7             : import (
       8             :         "context"
       9             :         "fmt"
      10             :         "slices"
      11             : 
      12             :         "github.com/cockroachdb/errors"
      13             :         "github.com/cockroachdb/pebble/internal/base"
      14             :         "github.com/cockroachdb/pebble/internal/invariants"
      15             :         "github.com/cockroachdb/pebble/internal/keyspan"
      16             :         "github.com/cockroachdb/pebble/internal/keyspan/keyspanimpl"
      17             :         "github.com/cockroachdb/pebble/internal/manifest"
      18             :         "github.com/cockroachdb/pebble/internal/treeprinter"
      19             :         "github.com/cockroachdb/pebble/objstorage"
      20             :         "github.com/cockroachdb/pebble/objstorage/remote"
      21             :         "github.com/cockroachdb/pebble/sstable"
      22             : )
      23             : 
      24             : const (
      25             :         // In skip-shared iteration mode, keys in levels greater than
      26             :         // sharedLevelsStart (i.e. lower in the LSM) are skipped. Keys
      27             :         // in sharedLevelsStart are returned iff they are not in a
      28             :         // shared file.
      29             :         sharedLevelsStart = remote.SharedLevelsStart
      30             : 
      31             :         // In skip-external iteration mode, keys in levels greater
      32             :         // than externalSkipStart are skipped. Keys in
      33             :         // externalSkipStart are returned iff they are not in an
      34             :         // external file.
      35             :         externalSkipStart = 6
      36             : )
      37             : 
      38             : // ErrInvalidSkipSharedIteration is returned by ScanInternal if it was called
      39             : // with a shared file visitor function, and a file in a shareable level (i.e.
      40             : // level >= sharedLevelsStart) was found to not be in shared storage according
      41             : // to objstorage.Provider, or not shareable for another reason such as for
      42             : // containing keys newer than the snapshot sequence number.
      43             : var ErrInvalidSkipSharedIteration = errors.New("pebble: cannot use skip-shared iteration due to non-shareable files in lower levels")
      44             : 
      45             : // SharedSSTMeta represents an sstable on shared storage that can be ingested
      46             : // by another pebble instance. This struct must contain all fields that are
      47             : // required for a Pebble instance to ingest a foreign sstable on shared storage,
      48             : // including constructing any relevant objstorage.Provider / remoteobjcat.Catalog
      49             : // data structures, as well as creating virtual FileMetadatas.
      50             : //
      51             : // Note that the Pebble instance creating and returning a SharedSSTMeta might
      52             : // not be the one that created the underlying sstable on shared storage to begin
      53             : // with; it's possible for a Pebble instance to reshare an sstable that was
      54             : // shared to it.
      55             : type SharedSSTMeta struct {
      56             :         // Backing is the shared object underlying this SST. Can be attached to an
      57             :         // objstorage.Provider.
      58             :         Backing objstorage.RemoteObjectBackingHandle
      59             : 
      60             :         // Smallest and Largest internal keys for the overall bounds. The kind and
      61             :         // SeqNum of these will reflect what is physically present on the source Pebble
      62             :         // instance's view of the sstable; it's up to the ingesting instance to set the
      63             :         // sequence number in the trailer to match the read-time sequence numbers
      64             :         // reserved for the level this SST is being ingested into. The Kind is expected
      65             :         // to remain unchanged by the ingesting instance.
      66             :         //
      67             :         // Note that these bounds could be narrower than the bounds of the underlying
      68             :         // sstable; ScanInternal is expected to truncate sstable bounds to the user key
      69             :         // bounds passed into that method.
      70             :         Smallest, Largest InternalKey
      71             : 
      72             :         // SmallestRangeKey and LargestRangeKey are internal keys that denote the
      73             :         // range key bounds of this sstable. Must lie within [Smallest, Largest].
      74             :         SmallestRangeKey, LargestRangeKey InternalKey
      75             : 
      76             :         // SmallestPointKey and LargestPointKey are internal keys that denote the
      77             :         // point key bounds of this sstable. Must lie within [Smallest, Largest].
      78             :         SmallestPointKey, LargestPointKey InternalKey
      79             : 
      80             :         // Level denotes the level at which this file was present at read time.
      81             :         // For files visited by ScanInternal, this value will only be 5 or 6.
      82             :         Level uint8
      83             : 
      84             :         // Size contains an estimate of the size of this sstable.
      85             :         Size uint64
      86             : 
      87             :         // fileNum at time of creation in the creator instance. Only used for
      88             :         // debugging/tests.
      89             :         fileNum base.FileNum
      90             : }
      91             : 
      92           2 : func (s *SharedSSTMeta) cloneFromFileMeta(f *fileMetadata) {
      93           2 :         *s = SharedSSTMeta{
      94           2 :                 Smallest:         f.Smallest.Clone(),
      95           2 :                 Largest:          f.Largest.Clone(),
      96           2 :                 SmallestRangeKey: f.SmallestRangeKey.Clone(),
      97           2 :                 LargestRangeKey:  f.LargestRangeKey.Clone(),
      98           2 :                 SmallestPointKey: f.SmallestPointKey.Clone(),
      99           2 :                 LargestPointKey:  f.LargestPointKey.Clone(),
     100           2 :                 Size:             f.Size,
     101           2 :                 fileNum:          f.FileNum,
     102           2 :         }
     103           2 : }
     104             : 
     105             : type sharedByLevel []SharedSSTMeta
     106             : 
     107           2 : func (s sharedByLevel) Len() int           { return len(s) }
     108           0 : func (s sharedByLevel) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
     109           2 : func (s sharedByLevel) Less(i, j int) bool { return s[i].Level < s[j].Level }
     110             : 
     111             : type pcIterPos int
     112             : 
     113             : const (
     114             :         pcIterPosCur pcIterPos = iota
     115             :         pcIterPosNext
     116             : )
     117             : 
     118             : // pointCollapsingIterator is an internalIterator that collapses point keys and
     119             : // returns at most one point internal key for each user key. Merges and
     120             : // SingleDels are not supported and result in a panic if encountered. Point keys
     121             : // deleted by rangedels are considered shadowed and not exposed.
     122             : //
     123             : // Only used in ScanInternal to return at most one internal key per user key.
     124             : type pointCollapsingIterator struct {
     125             :         iter     keyspan.InterleavingIter
     126             :         pos      pcIterPos
     127             :         comparer *base.Comparer
     128             :         merge    base.Merge
     129             :         err      error
     130             :         seqNum   base.SeqNum
     131             :         // The current position of `iter`. Always owned by the underlying iter.
     132             :         iterKV *base.InternalKV
     133             :         // The last saved key. findNextEntry and similar methods are expected to save
     134             :         // the current value of iterKey to savedKey if they're iterating away from the
     135             :         // current key but still need to retain it. See comments in findNextEntry on
     136             :         // how this field is used.
     137             :         //
     138             :         // At the end of a positioning call:
     139             :         //  - if pos == pcIterPosNext, iterKey is pointing to the next user key owned
     140             :         //    by `iter` while savedKey is holding a copy to our current key.
     141             :         //  - If pos == pcIterPosCur, iterKey is pointing to an `iter`-owned current
     142             :         //    key, and savedKey is either undefined or pointing to a version of the
     143             :         //    current key owned by this iterator (i.e. backed by savedKeyBuf).
     144             :         savedKey    InternalKey
     145             :         savedKeyBuf []byte
     146             :         // If fixedSeqNum is non-zero, all emitted points are verified to have this
     147             :         // fixed sequence number.
     148             :         fixedSeqNum base.SeqNum
     149             : }
     150             : 
     151           2 : func (p *pointCollapsingIterator) Span() *keyspan.Span {
     152           2 :         return p.iter.Span()
     153           2 : }
     154             : 
     155             : // SeekPrefixGE implements the InternalIterator interface.
     156             : func (p *pointCollapsingIterator) SeekPrefixGE(
     157             :         prefix, key []byte, flags base.SeekGEFlags,
     158           0 : ) *base.InternalKV {
     159           0 :         p.resetKey()
     160           0 :         p.iterKV = p.iter.SeekPrefixGE(prefix, key, flags)
     161           0 :         p.pos = pcIterPosCur
     162           0 :         if p.iterKV == nil {
     163           0 :                 return nil
     164           0 :         }
     165           0 :         return p.findNextEntry()
     166             : }
     167             : 
     168             : // SeekGE implements the InternalIterator interface.
     169           2 : func (p *pointCollapsingIterator) SeekGE(key []byte, flags base.SeekGEFlags) *base.InternalKV {
     170           2 :         p.resetKey()
     171           2 :         p.iterKV = p.iter.SeekGE(key, flags)
     172           2 :         p.pos = pcIterPosCur
     173           2 :         if p.iterKV == nil {
     174           2 :                 return nil
     175           2 :         }
     176           2 :         return p.findNextEntry()
     177             : }
     178             : 
     179             : // SeekLT implements the InternalIterator interface.
     180           0 : func (p *pointCollapsingIterator) SeekLT(key []byte, flags base.SeekLTFlags) *base.InternalKV {
     181           0 :         panic("unimplemented")
     182             : }
     183             : 
     184           2 : func (p *pointCollapsingIterator) resetKey() {
     185           2 :         p.savedKey.UserKey = p.savedKeyBuf[:0]
     186           2 :         p.savedKey.Trailer = 0
     187           2 :         p.iterKV = nil
     188           2 :         p.pos = pcIterPosCur
     189           2 : }
     190             : 
     191           2 : func (p *pointCollapsingIterator) verifySeqNum(kv *base.InternalKV) *base.InternalKV {
     192           2 :         if !invariants.Enabled {
     193           0 :                 return kv
     194           0 :         }
     195           2 :         if p.fixedSeqNum == 0 || kv == nil || kv.Kind() == InternalKeyKindRangeDelete {
     196           2 :                 return kv
     197           2 :         }
     198           0 :         if kv.SeqNum() != p.fixedSeqNum {
     199           0 :                 panic(fmt.Sprintf("expected foreign point key to have seqnum %d, got %d", p.fixedSeqNum, kv.SeqNum()))
     200             :         }
     201           0 :         return kv
     202             : }
     203             : 
     204             : // findNextEntry is called to return the next key. p.iter must be positioned at the
     205             : // start of the first user key we are interested in.
     206           2 : func (p *pointCollapsingIterator) findNextEntry() *base.InternalKV {
     207           2 :         p.saveKey()
     208           2 :         // Saves a comparison in the fast path
     209           2 :         firstIteration := true
     210           2 :         for p.iterKV != nil {
     211           2 :                 // NB: p.savedKey is either the current key (iff p.iterKV == firstKey),
     212           2 :                 // or the previous key.
     213           2 :                 if !firstIteration && !p.comparer.Equal(p.iterKV.K.UserKey, p.savedKey.UserKey) {
     214           2 :                         p.saveKey()
     215           2 :                         continue
     216             :                 }
     217           2 :                 firstIteration = false
     218           2 :                 if s := p.iter.Span(); s != nil && s.CoversAt(p.seqNum, p.iterKV.SeqNum()) {
     219           2 :                         // All future keys for this user key must be deleted.
     220           2 :                         if p.savedKey.Kind() == InternalKeyKindSingleDelete {
     221           0 :                                 panic("cannot process singledel key in point collapsing iterator")
     222             :                         }
     223             :                         // Fast forward to the next user key.
     224           2 :                         p.saveKey()
     225           2 :                         p.iterKV = p.iter.Next()
     226           2 :                         for p.iterKV != nil && p.savedKey.SeqNum() >= p.iterKV.SeqNum() && p.comparer.Equal(p.iterKV.K.UserKey, p.savedKey.UserKey) {
     227           2 :                                 p.iterKV = p.iter.Next()
     228           2 :                         }
     229           2 :                         continue
     230             :                 }
     231           2 :                 switch p.savedKey.Kind() {
     232           2 :                 case InternalKeyKindSet, InternalKeyKindDelete, InternalKeyKindSetWithDelete, InternalKeyKindDeleteSized:
     233           2 :                         // Note that we return SETs directly, even if they would otherwise get
     234           2 :                         // compacted into a Del to turn into a SetWithDelete. This is a fast
     235           2 :                         // path optimization that can break SINGLEDEL determinism. To lead to
     236           2 :                         // consistent SINGLEDEL behaviour, this iterator should *not* be used for
     237           2 :                         // a keyspace where SINGLEDELs could be in use. If this iterator observes
     238           2 :                         // a SINGLEDEL as the first internal key for a user key, it will panic.
     239           2 :                         //
     240           2 :                         // As p.value is a lazy value owned by the child iterator, we can thread
     241           2 :                         // it through without loading it into p.valueBuf.
     242           2 :                         //
     243           2 :                         // TODO(bilal): We can even avoid saving the key in this fast path if
     244           2 :                         // we are in a block where setHasSamePrefix = false in a v3 sstable,
     245           2 :                         // guaranteeing that there's only one internal key for each user key.
     246           2 :                         // Thread this logic through the sstable iterators and/or consider
     247           2 :                         // collapsing (ha) this logic into the sstable iterators that are aware
     248           2 :                         // of blocks and can determine user key changes without doing key saves
     249           2 :                         // or comparisons.
     250           2 :                         p.pos = pcIterPosCur
     251           2 :                         return p.verifySeqNum(p.iterKV)
     252           0 :                 case InternalKeyKindSingleDelete:
     253           0 :                         // Panic, as this iterator is not expected to observe single deletes.
     254           0 :                         panic("cannot process singledel key in point collapsing iterator")
     255           0 :                 case InternalKeyKindMerge:
     256           0 :                         // Panic, as this iterator is not expected to observe merges.
     257           0 :                         panic("cannot process merge key in point collapsing iterator")
     258           2 :                 case InternalKeyKindRangeDelete:
     259           2 :                         // These are interleaved by the interleaving iterator ahead of all points.
     260           2 :                         // We should pass them as-is, but also account for any points ahead of
     261           2 :                         // them.
     262           2 :                         p.pos = pcIterPosCur
     263           2 :                         return p.verifySeqNum(p.iterKV)
     264           0 :                 default:
     265           0 :                         panic(fmt.Sprintf("unexpected kind: %d", p.iterKV.Kind()))
     266             :                 }
     267             :         }
     268           1 :         p.resetKey()
     269           1 :         return nil
     270             : }
     271             : 
     272             : // First implements the InternalIterator interface.
     273           1 : func (p *pointCollapsingIterator) First() *base.InternalKV {
     274           1 :         p.resetKey()
     275           1 :         p.iterKV = p.iter.First()
     276           1 :         p.pos = pcIterPosCur
     277           1 :         if p.iterKV == nil {
     278           0 :                 return nil
     279           0 :         }
     280           1 :         return p.findNextEntry()
     281             : }
     282             : 
     283             : // Last implements the InternalIterator interface.
     284           0 : func (p *pointCollapsingIterator) Last() *base.InternalKV {
     285           0 :         panic("unimplemented")
     286             : }
     287             : 
     288           2 : func (p *pointCollapsingIterator) saveKey() {
     289           2 :         if p.iterKV == nil {
     290           1 :                 p.savedKey = InternalKey{UserKey: p.savedKeyBuf[:0]}
     291           1 :                 return
     292           1 :         }
     293           2 :         p.savedKeyBuf = append(p.savedKeyBuf[:0], p.iterKV.K.UserKey...)
     294           2 :         p.savedKey = InternalKey{UserKey: p.savedKeyBuf, Trailer: p.iterKV.K.Trailer}
     295             : }
     296             : 
     297             : // Next implements the InternalIterator interface.
     298           2 : func (p *pointCollapsingIterator) Next() *base.InternalKV {
     299           2 :         switch p.pos {
     300           2 :         case pcIterPosCur:
     301           2 :                 p.saveKey()
     302           2 :                 if p.iterKV != nil && p.iterKV.Kind() == InternalKeyKindRangeDelete {
     303           2 :                         // Step over the interleaved range delete and process the very next
     304           2 :                         // internal key, even if it's at the same user key. This is because a
     305           2 :                         // point for that user key has not been returned yet.
     306           2 :                         p.iterKV = p.iter.Next()
     307           2 :                         break
     308             :                 }
     309             :                 // Fast forward to the next user key.
     310           2 :                 kv := p.iter.Next()
     311           2 :                 // p.iterKV.SeqNum() >= key.SeqNum() is an optimization that allows us to
     312           2 :                 // use p.iterKV.SeqNum() < key.SeqNum() as a sign that the user key has
     313           2 :                 // changed, without needing to do the full key comparison.
     314           2 :                 for kv != nil && p.savedKey.SeqNum() >= kv.SeqNum() &&
     315           2 :                         p.comparer.Equal(p.savedKey.UserKey, kv.K.UserKey) {
     316           2 :                         kv = p.iter.Next()
     317           2 :                 }
     318           2 :                 if kv == nil {
     319           2 :                         // There are no keys to return.
     320           2 :                         p.resetKey()
     321           2 :                         return nil
     322           2 :                 }
     323           2 :                 p.iterKV = kv
     324           0 :         case pcIterPosNext:
     325           0 :                 p.pos = pcIterPosCur
     326             :         }
     327           2 :         if p.iterKV == nil {
     328           2 :                 p.resetKey()
     329           2 :                 return nil
     330           2 :         }
     331           2 :         return p.findNextEntry()
     332             : }
     333             : 
     334             : // NextPrefix implements the InternalIterator interface.
     335           0 : func (p *pointCollapsingIterator) NextPrefix(succKey []byte) *base.InternalKV {
     336           0 :         panic("unimplemented")
     337             : }
     338             : 
     339             : // Prev implements the InternalIterator interface.
     340           0 : func (p *pointCollapsingIterator) Prev() *base.InternalKV {
     341           0 :         panic("unimplemented")
     342             : }
     343             : 
     344             : // Error implements the InternalIterator interface.
     345           2 : func (p *pointCollapsingIterator) Error() error {
     346           2 :         if p.err != nil {
     347           0 :                 return p.err
     348           0 :         }
     349           2 :         return p.iter.Error()
     350             : }
     351             : 
     352             : // Close implements the InternalIterator interface.
     353           2 : func (p *pointCollapsingIterator) Close() error {
     354           2 :         return p.iter.Close()
     355           2 : }
     356             : 
     357             : // SetBounds implements the InternalIterator interface.
     358           0 : func (p *pointCollapsingIterator) SetBounds(lower, upper []byte) {
     359           0 :         p.resetKey()
     360           0 :         p.iter.SetBounds(lower, upper)
     361           0 : }
     362             : 
     363           0 : func (p *pointCollapsingIterator) SetContext(ctx context.Context) {
     364           0 :         p.iter.SetContext(ctx)
     365           0 : }
     366             : 
     367             : // DebugTree is part of the InternalIterator interface.
     368           0 : func (p *pointCollapsingIterator) DebugTree(tp treeprinter.Node) {
     369           0 :         n := tp.Childf("%T(%p)", p, p)
     370           0 :         p.iter.DebugTree(n)
     371           0 : }
     372             : 
     373             : // String implements the InternalIterator interface.
     374           0 : func (p *pointCollapsingIterator) String() string {
     375           0 :         return p.iter.String()
     376           0 : }
     377             : 
     378             : var _ internalIterator = &pointCollapsingIterator{}
     379             : 
     380             : // IteratorLevelKind is used to denote whether the current ScanInternal iterator
     381             : // is unknown, belongs to a flushable, or belongs to an LSM level type.
     382             : type IteratorLevelKind int8
     383             : 
     384             : const (
     385             :         // IteratorLevelUnknown indicates an unknown LSM level.
     386             :         IteratorLevelUnknown IteratorLevelKind = iota
     387             :         // IteratorLevelLSM indicates an LSM level.
     388             :         IteratorLevelLSM
     389             :         // IteratorLevelFlushable indicates a flushable (i.e. memtable).
     390             :         IteratorLevelFlushable
     391             : )
     392             : 
     393             : // IteratorLevel is used with scanInternalIterator to surface additional iterator-specific info where possible.
     394             : // Note: this is struct is only provided for point keys.
     395             : type IteratorLevel struct {
     396             :         Kind IteratorLevelKind
     397             :         // FlushableIndex indicates the position within the flushable queue of this level.
     398             :         // Only valid if kind == IteratorLevelFlushable.
     399             :         FlushableIndex int
     400             :         // The level within the LSM. Only valid if Kind == IteratorLevelLSM.
     401             :         Level int
     402             :         // Sublevel is only valid if Kind == IteratorLevelLSM and Level == 0.
     403             :         Sublevel int
     404             : }
     405             : 
     406             : // scanInternalIterator is an iterator that returns all internal keys, including
     407             : // tombstones. For instance, an InternalKeyKindDelete would be returned as an
     408             : // InternalKeyKindDelete instead of the iterator skipping over to the next key.
     409             : // Internal keys within a user key are collapsed, eg. if there are two SETs, the
     410             : // one with the higher sequence is returned. Useful if an external user of Pebble
     411             : // needs to observe and rebuild Pebble's history of internal keys, such as in
     412             : // node-to-node replication. For use with {db,snapshot}.ScanInternal().
     413             : //
     414             : // scanInternalIterator is expected to ignore point keys deleted by range
     415             : // deletions, and range keys shadowed by a range key unset or delete, however it
     416             : // *must* return the range delete as well as the range key unset/delete that did
     417             : // the shadowing.
     418             : type scanInternalIterator struct {
     419             :         ctx             context.Context
     420             :         db              *DB
     421             :         opts            scanInternalOptions
     422             :         comparer        *base.Comparer
     423             :         merge           Merge
     424             :         iter            internalIterator
     425             :         readState       *readState
     426             :         version         *version
     427             :         rangeKey        *iteratorRangeKeyState
     428             :         pointKeyIter    internalIterator
     429             :         iterKV          *base.InternalKV
     430             :         alloc           *iterAlloc
     431             :         newIters        tableNewIters
     432             :         newIterRangeKey keyspanimpl.TableNewSpanIter
     433             :         seqNum          base.SeqNum
     434             :         iterLevels      []IteratorLevel
     435             :         mergingIter     *mergingIter
     436             : 
     437             :         // boundsBuf holds two buffers used to store the lower and upper bounds.
     438             :         // Whenever the InternalIterator's bounds change, the new bounds are copied
     439             :         // into boundsBuf[boundsBufIdx]. The two bounds share a slice to reduce
     440             :         // allocations. opts.LowerBound and opts.UpperBound point into this slice.
     441             :         boundsBuf    [2][]byte
     442             :         boundsBufIdx int
     443             : }
     444             : 
     445             : // truncateExternalFile truncates an External file's [SmallestUserKey,
     446             : // LargestUserKey] fields to [lower, upper). A ExternalFile is
     447             : // produced that is suitable for external consumption by other Pebble
     448             : // instances.
     449             : //
     450             : // truncateSharedFile reads the file to try to create the smallest
     451             : // possible bounds.  Here, we blindly truncate them. This may mean we
     452             : // include this SST in iterations it isn't really needed in. Since we
     453             : // don't expect External files to be long-lived in the pebble
     454             : // instance, We think this is OK.
     455             : //
     456             : // TODO(ssd) 2024-01-26: Potentially de-duplicate with
     457             : // truncateSharedFile.
     458             : func (d *DB) truncateExternalFile(
     459             :         ctx context.Context,
     460             :         lower, upper []byte,
     461             :         level int,
     462             :         file *fileMetadata,
     463             :         objMeta objstorage.ObjectMetadata,
     464           1 : ) (*ExternalFile, error) {
     465           1 :         cmp := d.cmp
     466           1 :         sst := &ExternalFile{
     467           1 :                 Level:           uint8(level),
     468           1 :                 ObjName:         objMeta.Remote.CustomObjectName,
     469           1 :                 Locator:         objMeta.Remote.Locator,
     470           1 :                 HasPointKey:     file.HasPointKeys,
     471           1 :                 HasRangeKey:     file.HasRangeKeys,
     472           1 :                 Size:            file.Size,
     473           1 :                 SyntheticSuffix: slices.Clone(file.SyntheticSuffix),
     474           1 :         }
     475           1 :         if file.SyntheticPrefix.IsSet() {
     476           0 :                 sst.SyntheticPrefix = slices.Clone(sst.SyntheticPrefix)
     477           0 :         }
     478             : 
     479           1 :         needsLowerTruncate := cmp(lower, file.Smallest.UserKey) > 0
     480           1 :         if needsLowerTruncate {
     481           1 :                 sst.StartKey = slices.Clone(lower)
     482           1 :         } else {
     483           1 :                 sst.StartKey = slices.Clone(file.Smallest.UserKey)
     484           1 :         }
     485             : 
     486           1 :         cmpUpper := cmp(upper, file.Largest.UserKey)
     487           1 :         needsUpperTruncate := cmpUpper < 0
     488           1 :         if needsUpperTruncate {
     489           0 :                 sst.EndKey = slices.Clone(upper)
     490           0 :                 sst.EndKeyIsInclusive = false
     491           1 :         } else {
     492           1 :                 sst.EndKey = slices.Clone(file.Largest.UserKey)
     493           1 :                 sst.EndKeyIsInclusive = !file.Largest.IsExclusiveSentinel()
     494           1 :         }
     495             : 
     496           1 :         if cmp(sst.StartKey, sst.EndKey) > 0 {
     497           0 :                 return nil, base.AssertionFailedf("pebble: invalid external file bounds after truncation [%q, %q)", sst.StartKey, sst.EndKey)
     498           0 :         }
     499             : 
     500           1 :         if cmp(sst.StartKey, sst.EndKey) == 0 && !sst.EndKeyIsInclusive {
     501           0 :                 return nil, base.AssertionFailedf("pebble: invalid external file bounds after truncation [%q, %q)", sst.StartKey, sst.EndKey)
     502           0 :         }
     503             : 
     504           1 :         return sst, nil
     505             : }
     506             : 
     507             : // truncateSharedFile truncates a shared file's [Smallest, Largest] fields to
     508             : // [lower, upper), potentially opening iterators on the file to find keys within
     509             : // the requested bounds. A SharedSSTMeta is produced that is suitable for
     510             : // external consumption by other Pebble instances. If shouldSkip is true, this
     511             : // file does not contain any keys in [lower, upper) and can be skipped.
     512             : //
     513             : // TODO(bilal): If opening iterators and doing reads in this method is too
     514             : // inefficient, consider producing non-tight file bounds instead.
     515             : func (d *DB) truncateSharedFile(
     516             :         ctx context.Context,
     517             :         lower, upper []byte,
     518             :         level int,
     519             :         file *fileMetadata,
     520             :         objMeta objstorage.ObjectMetadata,
     521           2 : ) (sst *SharedSSTMeta, shouldSkip bool, err error) {
     522           2 :         cmp := d.cmp
     523           2 :         sst = &SharedSSTMeta{}
     524           2 :         sst.cloneFromFileMeta(file)
     525           2 :         sst.Level = uint8(level)
     526           2 :         sst.Backing, err = d.objProvider.RemoteObjectBacking(&objMeta)
     527           2 :         if err != nil {
     528           0 :                 return nil, false, err
     529           0 :         }
     530           2 :         needsLowerTruncate := cmp(lower, file.Smallest.UserKey) > 0
     531           2 :         needsUpperTruncate := cmp(upper, file.Largest.UserKey) < 0 || (cmp(upper, file.Largest.UserKey) == 0 && !file.Largest.IsExclusiveSentinel())
     532           2 :         // Fast path: file is entirely within [lower, upper).
     533           2 :         if !needsLowerTruncate && !needsUpperTruncate {
     534           2 :                 return sst, false, nil
     535           2 :         }
     536             : 
     537             :         // We will need to truncate file bounds in at least one direction. Open all
     538             :         // relevant iterators.
     539           2 :         iters, err := d.newIters(ctx, file, &IterOptions{
     540           2 :                 LowerBound: lower,
     541           2 :                 UpperBound: upper,
     542           2 :                 level:      manifest.Level(level),
     543           2 :         }, internalIterOpts{}, iterPointKeys|iterRangeDeletions|iterRangeKeys)
     544           2 :         if err != nil {
     545           0 :                 return nil, false, err
     546           0 :         }
     547           2 :         defer iters.CloseAll()
     548           2 :         iter := iters.point
     549           2 :         rangeDelIter := iters.rangeDeletion
     550           2 :         rangeKeyIter := iters.rangeKey
     551           2 :         if rangeDelIter != nil {
     552           2 :                 rangeDelIter = keyspan.Truncate(cmp, rangeDelIter, base.UserKeyBoundsEndExclusive(lower, upper))
     553           2 :         }
     554           2 :         if rangeKeyIter != nil {
     555           2 :                 rangeKeyIter = keyspan.Truncate(cmp, rangeKeyIter, base.UserKeyBoundsEndExclusive(lower, upper))
     556           2 :         }
     557             :         // Check if we need to truncate on the left side. This means finding a new
     558             :         // LargestPointKey and LargestRangeKey that is >= lower.
     559           2 :         if needsLowerTruncate {
     560           2 :                 sst.SmallestPointKey.UserKey = sst.SmallestPointKey.UserKey[:0]
     561           2 :                 sst.SmallestPointKey.Trailer = 0
     562           2 :                 kv := iter.SeekGE(lower, base.SeekGEFlagsNone)
     563           2 :                 foundPointKey := kv != nil
     564           2 :                 if kv != nil {
     565           2 :                         sst.SmallestPointKey.CopyFrom(kv.K)
     566           2 :                 }
     567           2 :                 if rangeDelIter != nil {
     568           2 :                         if span, err := rangeDelIter.SeekGE(lower); err != nil {
     569           0 :                                 return nil, false, err
     570           2 :                         } else if span != nil && (len(sst.SmallestPointKey.UserKey) == 0 || base.InternalCompare(cmp, span.SmallestKey(), sst.SmallestPointKey) < 0) {
     571           2 :                                 sst.SmallestPointKey.CopyFrom(span.SmallestKey())
     572           2 :                                 foundPointKey = true
     573           2 :                         }
     574             :                 }
     575           2 :                 if !foundPointKey {
     576           2 :                         // There are no point keys in the span we're interested in.
     577           2 :                         sst.SmallestPointKey = InternalKey{}
     578           2 :                         sst.LargestPointKey = InternalKey{}
     579           2 :                 }
     580           2 :                 sst.SmallestRangeKey.UserKey = sst.SmallestRangeKey.UserKey[:0]
     581           2 :                 sst.SmallestRangeKey.Trailer = 0
     582           2 :                 if rangeKeyIter != nil {
     583           2 :                         span, err := rangeKeyIter.SeekGE(lower)
     584           2 :                         switch {
     585           0 :                         case err != nil:
     586           0 :                                 return nil, false, err
     587           2 :                         case span != nil:
     588           2 :                                 sst.SmallestRangeKey.CopyFrom(span.SmallestKey())
     589           2 :                         default:
     590           2 :                                 // There are no range keys in the span we're interested in.
     591           2 :                                 sst.SmallestRangeKey = InternalKey{}
     592           2 :                                 sst.LargestRangeKey = InternalKey{}
     593             :                         }
     594             :                 }
     595             :         }
     596             :         // Check if we need to truncate on the right side. This means finding a new
     597             :         // LargestPointKey and LargestRangeKey that is < upper.
     598           2 :         if needsUpperTruncate {
     599           2 :                 sst.LargestPointKey.UserKey = sst.LargestPointKey.UserKey[:0]
     600           2 :                 sst.LargestPointKey.Trailer = 0
     601           2 :                 kv := iter.SeekLT(upper, base.SeekLTFlagsNone)
     602           2 :                 foundPointKey := kv != nil
     603           2 :                 if kv != nil {
     604           2 :                         sst.LargestPointKey.CopyFrom(kv.K)
     605           2 :                 }
     606           2 :                 if rangeDelIter != nil {
     607           2 :                         if span, err := rangeDelIter.SeekLT(upper); err != nil {
     608           0 :                                 return nil, false, err
     609           2 :                         } else if span != nil && (len(sst.LargestPointKey.UserKey) == 0 || base.InternalCompare(cmp, span.LargestKey(), sst.LargestPointKey) > 0) {
     610           2 :                                 sst.LargestPointKey.CopyFrom(span.LargestKey())
     611           2 :                                 foundPointKey = true
     612           2 :                         }
     613             :                 }
     614           2 :                 if !foundPointKey {
     615           2 :                         // There are no point keys in the span we're interested in.
     616           2 :                         sst.SmallestPointKey = InternalKey{}
     617           2 :                         sst.LargestPointKey = InternalKey{}
     618           2 :                 }
     619           2 :                 sst.LargestRangeKey.UserKey = sst.LargestRangeKey.UserKey[:0]
     620           2 :                 sst.LargestRangeKey.Trailer = 0
     621           2 :                 if rangeKeyIter != nil {
     622           2 :                         span, err := rangeKeyIter.SeekLT(upper)
     623           2 :                         switch {
     624           0 :                         case err != nil:
     625           0 :                                 return nil, false, err
     626           2 :                         case span != nil:
     627           2 :                                 sst.LargestRangeKey.CopyFrom(span.LargestKey())
     628           2 :                         default:
     629           2 :                                 // There are no range keys in the span we're interested in.
     630           2 :                                 sst.SmallestRangeKey = InternalKey{}
     631           2 :                                 sst.LargestRangeKey = InternalKey{}
     632             :                         }
     633             :                 }
     634             :         }
     635             :         // Set overall bounds based on {Smallest,Largest}{Point,Range}Key.
     636           2 :         switch {
     637           2 :         case len(sst.SmallestRangeKey.UserKey) == 0:
     638           2 :                 sst.Smallest = sst.SmallestPointKey
     639           2 :         case len(sst.SmallestPointKey.UserKey) == 0:
     640           2 :                 sst.Smallest = sst.SmallestRangeKey
     641           2 :         default:
     642           2 :                 sst.Smallest = sst.SmallestPointKey
     643           2 :                 if base.InternalCompare(cmp, sst.SmallestRangeKey, sst.SmallestPointKey) < 0 {
     644           2 :                         sst.Smallest = sst.SmallestRangeKey
     645           2 :                 }
     646             :         }
     647           2 :         switch {
     648           2 :         case len(sst.LargestRangeKey.UserKey) == 0:
     649           2 :                 sst.Largest = sst.LargestPointKey
     650           2 :         case len(sst.LargestPointKey.UserKey) == 0:
     651           2 :                 sst.Largest = sst.LargestRangeKey
     652           2 :         default:
     653           2 :                 sst.Largest = sst.LargestPointKey
     654           2 :                 if base.InternalCompare(cmp, sst.LargestRangeKey, sst.LargestPointKey) > 0 {
     655           2 :                         sst.Largest = sst.LargestRangeKey
     656           2 :                 }
     657             :         }
     658             :         // On rare occasion, a file might overlap with [lower, upper) but not actually
     659             :         // have any keys within those bounds. Skip such files.
     660           2 :         if len(sst.Smallest.UserKey) == 0 {
     661           2 :                 return nil, true, nil
     662           2 :         }
     663           2 :         sst.Size, err = d.tableCache.estimateSize(file, sst.Smallest.UserKey, sst.Largest.UserKey)
     664           2 :         if err != nil {
     665           0 :                 return nil, false, err
     666           0 :         }
     667             :         // On occasion, estimateSize gives us a low estimate, i.e. a 0 file size. This
     668             :         // can cause panics in places where we divide by file sizes. Correct for it
     669             :         // here.
     670           2 :         if sst.Size == 0 {
     671           2 :                 sst.Size = 1
     672           2 :         }
     673           2 :         return sst, false, nil
     674             : }
     675             : 
     676             : func scanInternalImpl(
     677             :         ctx context.Context, lower, upper []byte, iter *scanInternalIterator, opts *scanInternalOptions,
     678           2 : ) error {
     679           2 :         if opts.visitSharedFile != nil && (lower == nil || upper == nil) {
     680           0 :                 panic("lower and upper bounds must be specified in skip-shared iteration mode")
     681             :         }
     682           2 :         if opts.visitSharedFile != nil && opts.visitExternalFile != nil {
     683           0 :                 return base.AssertionFailedf("cannot provide both a shared-file and external-file visitor")
     684           0 :         }
     685             : 
     686             :         // Before starting iteration, check if any files in levels sharedLevelsStart
     687             :         // and below are *not* shared. Error out if that is the case, as skip-shared
     688             :         // iteration will not produce a consistent point-in-time view of this range
     689             :         // of keys. For files that are shared, call visitSharedFile with a truncated
     690             :         // version of that file.
     691           2 :         cmp := iter.comparer.Compare
     692           2 :         provider := iter.db.ObjProvider()
     693           2 :         seqNum := iter.seqNum
     694           2 :         current := iter.version
     695           2 :         if current == nil {
     696           2 :                 current = iter.readState.current
     697           2 :         }
     698             : 
     699           2 :         if opts.visitSharedFile != nil || opts.visitExternalFile != nil {
     700           2 :                 if provider == nil {
     701           0 :                         panic("expected non-nil Provider in skip-shared iteration mode")
     702             :                 }
     703             : 
     704           2 :                 firstLevelWithRemote := opts.skipLevelForOpts()
     705           2 :                 for level := firstLevelWithRemote; level < numLevels; level++ {
     706           2 :                         files := current.Levels[level].Iter()
     707           2 :                         for f := files.SeekGE(cmp, lower); f != nil && cmp(f.Smallest.UserKey, upper) < 0; f = files.Next() {
     708           2 :                                 if cmp(lower, f.Largest.UserKey) == 0 && f.Largest.IsExclusiveSentinel() {
     709           0 :                                         continue
     710             :                                 }
     711             : 
     712           2 :                                 var objMeta objstorage.ObjectMetadata
     713           2 :                                 var err error
     714           2 :                                 objMeta, err = provider.Lookup(fileTypeTable, f.FileBacking.DiskFileNum)
     715           2 :                                 if err != nil {
     716           0 :                                         return err
     717           0 :                                 }
     718             : 
     719             :                                 // We allow a mix of files at the first level.
     720           2 :                                 if level != firstLevelWithRemote {
     721           2 :                                         if !objMeta.IsShared() && !objMeta.IsExternal() {
     722           0 :                                                 return errors.Wrapf(ErrInvalidSkipSharedIteration, "file %s is not shared or external", objMeta.DiskFileNum)
     723           0 :                                         }
     724             :                                 }
     725             : 
     726           2 :                                 if objMeta.IsShared() && opts.visitSharedFile == nil {
     727           0 :                                         return errors.Wrapf(ErrInvalidSkipSharedIteration, "shared file is present but no shared file visitor is defined")
     728           0 :                                 }
     729             : 
     730           2 :                                 if objMeta.IsExternal() && opts.visitExternalFile == nil {
     731           1 :                                         return errors.Wrapf(ErrInvalidSkipSharedIteration, "external file is present but no external file visitor is defined")
     732           1 :                                 }
     733             : 
     734           2 :                                 if !base.Visible(f.LargestSeqNum, seqNum, base.SeqNumMax) {
     735           1 :                                         return errors.Wrapf(ErrInvalidSkipSharedIteration, "file %s contains keys newer than snapshot", objMeta.DiskFileNum)
     736           1 :                                 }
     737             : 
     738           2 :                                 if level != firstLevelWithRemote && (!objMeta.IsShared() && !objMeta.IsExternal()) {
     739           0 :                                         return errors.Wrapf(ErrInvalidSkipSharedIteration, "file %s is not shared or external", objMeta.DiskFileNum)
     740           0 :                                 }
     741             : 
     742           2 :                                 if objMeta.IsShared() {
     743           2 :                                         var sst *SharedSSTMeta
     744           2 :                                         var skip bool
     745           2 :                                         sst, skip, err = iter.db.truncateSharedFile(ctx, lower, upper, level, f, objMeta)
     746           2 :                                         if err != nil {
     747           0 :                                                 return err
     748           0 :                                         }
     749           2 :                                         if skip {
     750           2 :                                                 continue
     751             :                                         }
     752           2 :                                         if err = opts.visitSharedFile(sst); err != nil {
     753           0 :                                                 return err
     754           0 :                                         }
     755           1 :                                 } else if objMeta.IsExternal() {
     756           1 :                                         sst, err := iter.db.truncateExternalFile(ctx, lower, upper, level, f, objMeta)
     757           1 :                                         if err != nil {
     758           0 :                                                 return err
     759           0 :                                         }
     760           1 :                                         if err := opts.visitExternalFile(sst); err != nil {
     761           0 :                                                 return err
     762           0 :                                         }
     763             :                                 }
     764             : 
     765             :                         }
     766             :                 }
     767             :         }
     768             : 
     769           2 :         for valid := iter.seekGE(lower); valid && iter.error() == nil; valid = iter.next() {
     770           2 :                 key := iter.unsafeKey()
     771           2 : 
     772           2 :                 if opts.rateLimitFunc != nil {
     773           0 :                         if err := opts.rateLimitFunc(key, iter.lazyValue()); err != nil {
     774           0 :                                 return err
     775           0 :                         }
     776             :                 }
     777             : 
     778           2 :                 switch key.Kind() {
     779           2 :                 case InternalKeyKindRangeKeyDelete, InternalKeyKindRangeKeyUnset, InternalKeyKindRangeKeySet:
     780           2 :                         if opts.visitRangeKey != nil {
     781           2 :                                 span := iter.unsafeSpan()
     782           2 :                                 // NB: The caller isn't interested in the sequence numbers of these
     783           2 :                                 // range keys. Rather, the caller wants them to be in trailer order
     784           2 :                                 // _after_ zeroing of sequence numbers. Copy span.Keys, sort it, and then
     785           2 :                                 // call visitRangeKey.
     786           2 :                                 keysCopy := make([]keyspan.Key, len(span.Keys))
     787           2 :                                 for i := range span.Keys {
     788           2 :                                         keysCopy[i].CopyFrom(span.Keys[i])
     789           2 :                                         keysCopy[i].Trailer = base.MakeTrailer(0, span.Keys[i].Kind())
     790           2 :                                 }
     791           2 :                                 keyspan.SortKeysByTrailer(&keysCopy)
     792           2 :                                 if err := opts.visitRangeKey(span.Start, span.End, keysCopy); err != nil {
     793           0 :                                         return err
     794           0 :                                 }
     795             :                         }
     796           2 :                 case InternalKeyKindRangeDelete:
     797           2 :                         if opts.visitRangeDel != nil {
     798           2 :                                 rangeDel := iter.unsafeRangeDel()
     799           2 :                                 if err := opts.visitRangeDel(rangeDel.Start, rangeDel.End, rangeDel.LargestSeqNum()); err != nil {
     800           0 :                                         return err
     801           0 :                                 }
     802             :                         }
     803           2 :                 default:
     804           2 :                         if opts.visitPointKey != nil {
     805           2 :                                 var info IteratorLevel
     806           2 :                                 if len(iter.mergingIter.heap.items) > 0 {
     807           2 :                                         mergingIterIdx := iter.mergingIter.heap.items[0].index
     808           2 :                                         info = iter.iterLevels[mergingIterIdx]
     809           2 :                                 } else {
     810           0 :                                         info = IteratorLevel{Kind: IteratorLevelUnknown}
     811           0 :                                 }
     812           2 :                                 val := iter.lazyValue()
     813           2 :                                 if err := opts.visitPointKey(key, val, info); err != nil {
     814           0 :                                         return err
     815           0 :                                 }
     816             :                         }
     817             :                 }
     818             :         }
     819             : 
     820           2 :         return nil
     821             : }
     822             : 
     823           2 : func (opts *scanInternalOptions) skipLevelForOpts() int {
     824           2 :         if opts.visitSharedFile != nil {
     825           2 :                 return sharedLevelsStart
     826           2 :         }
     827           1 :         if opts.visitExternalFile != nil {
     828           1 :                 return externalSkipStart
     829           1 :         }
     830           1 :         return numLevels
     831             : }
     832             : 
     833             : // constructPointIter constructs a merging iterator and sets i.iter to it.
     834             : func (i *scanInternalIterator) constructPointIter(
     835             :         categoryAndQoS sstable.CategoryAndQoS, memtables flushableList, buf *iterAlloc,
     836           2 : ) error {
     837           2 :         // Merging levels and levels from iterAlloc.
     838           2 :         mlevels := buf.mlevels[:0]
     839           2 :         levels := buf.levels[:0]
     840           2 : 
     841           2 :         // We compute the number of levels needed ahead of time and reallocate a slice if
     842           2 :         // the array from the iterAlloc isn't large enough. Doing this allocation once
     843           2 :         // should improve the performance.
     844           2 :         numMergingLevels := len(memtables)
     845           2 :         numLevelIters := 0
     846           2 : 
     847           2 :         current := i.version
     848           2 :         if current == nil {
     849           2 :                 current = i.readState.current
     850           2 :         }
     851           2 :         numMergingLevels += len(current.L0SublevelFiles)
     852           2 :         numLevelIters += len(current.L0SublevelFiles)
     853           2 : 
     854           2 :         skipStart := i.opts.skipLevelForOpts()
     855           2 :         for level := 1; level < len(current.Levels); level++ {
     856           2 :                 if current.Levels[level].Empty() {
     857           2 :                         continue
     858             :                 }
     859           2 :                 if level > skipStart {
     860           2 :                         continue
     861             :                 }
     862           2 :                 numMergingLevels++
     863           2 :                 numLevelIters++
     864             :         }
     865             : 
     866           2 :         if numMergingLevels > cap(mlevels) {
     867           1 :                 mlevels = make([]mergingIterLevel, 0, numMergingLevels)
     868           1 :         }
     869           2 :         if numLevelIters > cap(levels) {
     870           1 :                 levels = make([]levelIter, 0, numLevelIters)
     871           1 :         }
     872             :         // TODO(bilal): Push these into the iterAlloc buf.
     873           2 :         var rangeDelMiter keyspanimpl.MergingIter
     874           2 :         rangeDelIters := make([]keyspan.FragmentIterator, 0, numMergingLevels)
     875           2 :         rangeDelLevels := make([]keyspanimpl.LevelIter, 0, numLevelIters)
     876           2 : 
     877           2 :         i.iterLevels = make([]IteratorLevel, numMergingLevels)
     878           2 :         mlevelsIndex := 0
     879           2 : 
     880           2 :         // Next are the memtables.
     881           2 :         for j := len(memtables) - 1; j >= 0; j-- {
     882           2 :                 mem := memtables[j]
     883           2 :                 mlevels = append(mlevels, mergingIterLevel{
     884           2 :                         iter: mem.newIter(&i.opts.IterOptions),
     885           2 :                 })
     886           2 :                 i.iterLevels[mlevelsIndex] = IteratorLevel{
     887           2 :                         Kind:           IteratorLevelFlushable,
     888           2 :                         FlushableIndex: j,
     889           2 :                 }
     890           2 :                 mlevelsIndex++
     891           2 :                 if rdi := mem.newRangeDelIter(&i.opts.IterOptions); rdi != nil {
     892           2 :                         rangeDelIters = append(rangeDelIters, rdi)
     893           2 :                 }
     894             :         }
     895             : 
     896             :         // Next are the file levels: L0 sub-levels followed by lower levels.
     897           2 :         levelsIndex := len(levels)
     898           2 :         mlevels = mlevels[:numMergingLevels]
     899           2 :         levels = levels[:numLevelIters]
     900           2 :         rangeDelLevels = rangeDelLevels[:numLevelIters]
     901           2 :         i.opts.IterOptions.snapshotForHideObsoletePoints = i.seqNum
     902           2 :         i.opts.IterOptions.CategoryAndQoS = categoryAndQoS
     903           2 :         addLevelIterForFiles := func(files manifest.LevelIterator, level manifest.Level) {
     904           2 :                 li := &levels[levelsIndex]
     905           2 :                 rli := &rangeDelLevels[levelsIndex]
     906           2 : 
     907           2 :                 li.init(
     908           2 :                         i.ctx, i.opts.IterOptions, i.comparer, i.newIters, files, level,
     909           2 :                         internalIterOpts{})
     910           2 :                 mlevels[mlevelsIndex].iter = li
     911           2 :                 rli.Init(keyspan.SpanIterOptions{RangeKeyFilters: i.opts.RangeKeyFilters},
     912           2 :                         i.comparer.Compare, tableNewRangeDelIter(i.ctx, i.newIters), files, level,
     913           2 :                         manifest.KeyTypePoint)
     914           2 :                 rangeDelIters = append(rangeDelIters, rli)
     915           2 : 
     916           2 :                 levelsIndex++
     917           2 :                 mlevelsIndex++
     918           2 :         }
     919             : 
     920           2 :         for j := len(current.L0SublevelFiles) - 1; j >= 0; j-- {
     921           2 :                 i.iterLevels[mlevelsIndex] = IteratorLevel{
     922           2 :                         Kind:     IteratorLevelLSM,
     923           2 :                         Level:    0,
     924           2 :                         Sublevel: j,
     925           2 :                 }
     926           2 :                 addLevelIterForFiles(current.L0SublevelFiles[j].Iter(), manifest.L0Sublevel(j))
     927           2 :         }
     928             :         // Add level iterators for the non-empty non-L0 levels.
     929           2 :         for level := 1; level < numLevels; level++ {
     930           2 :                 if current.Levels[level].Empty() {
     931           2 :                         continue
     932             :                 }
     933             : 
     934           2 :                 if level > skipStart {
     935           2 :                         continue
     936             :                 }
     937           2 :                 i.iterLevels[mlevelsIndex] = IteratorLevel{Kind: IteratorLevelLSM, Level: level}
     938           2 :                 levIter := current.Levels[level].Iter()
     939           2 :                 if level == skipStart {
     940           2 :                         nonRemoteFiles := make([]*manifest.FileMetadata, 0)
     941           2 :                         for f := levIter.First(); f != nil; f = levIter.Next() {
     942           2 :                                 meta, err := i.db.objProvider.Lookup(fileTypeTable, f.FileBacking.DiskFileNum)
     943           2 :                                 if err != nil {
     944           0 :                                         return err
     945           0 :                                 }
     946           2 :                                 if (meta.IsShared() && i.opts.visitSharedFile != nil) ||
     947           2 :                                         (meta.IsExternal() && i.opts.visitExternalFile != nil) {
     948           2 :                                         // Skip this file.
     949           2 :                                         continue
     950             :                                 }
     951           1 :                                 nonRemoteFiles = append(nonRemoteFiles, f)
     952             :                         }
     953           2 :                         levSlice := manifest.NewLevelSliceKeySorted(i.db.cmp, nonRemoteFiles)
     954           2 :                         levIter = levSlice.Iter()
     955             :                 }
     956             : 
     957           2 :                 addLevelIterForFiles(levIter, manifest.Level(level))
     958             :         }
     959             : 
     960           2 :         buf.merging.init(&i.opts.IterOptions, &InternalIteratorStats{}, i.comparer.Compare, i.comparer.Split, mlevels...)
     961           2 :         buf.merging.snapshot = i.seqNum
     962           2 :         rangeDelMiter.Init(i.comparer, keyspan.VisibleTransform(i.seqNum), new(keyspanimpl.MergingBuffers), rangeDelIters...)
     963           2 : 
     964           2 :         if i.opts.includeObsoleteKeys {
     965           1 :                 iiter := &keyspan.InterleavingIter{}
     966           1 :                 iiter.Init(i.comparer, &buf.merging, &rangeDelMiter,
     967           1 :                         keyspan.InterleavingIterOpts{
     968           1 :                                 LowerBound: i.opts.LowerBound,
     969           1 :                                 UpperBound: i.opts.UpperBound,
     970           1 :                         })
     971           1 :                 i.pointKeyIter = iiter
     972           2 :         } else {
     973           2 :                 pcIter := &pointCollapsingIterator{
     974           2 :                         comparer: i.comparer,
     975           2 :                         merge:    i.merge,
     976           2 :                         seqNum:   i.seqNum,
     977           2 :                 }
     978           2 :                 pcIter.iter.Init(i.comparer, &buf.merging, &rangeDelMiter, keyspan.InterleavingIterOpts{
     979           2 :                         LowerBound: i.opts.LowerBound,
     980           2 :                         UpperBound: i.opts.UpperBound,
     981           2 :                 })
     982           2 :                 i.pointKeyIter = pcIter
     983           2 :         }
     984           2 :         i.iter = i.pointKeyIter
     985           2 :         return nil
     986             : }
     987             : 
     988             : // constructRangeKeyIter constructs the range-key iterator stack, populating
     989             : // i.rangeKey.rangeKeyIter with the resulting iterator. This is similar to
     990             : // Iterator.constructRangeKeyIter, except it doesn't handle batches and ensures
     991             : // iterConfig does *not* elide unsets/deletes.
     992           2 : func (i *scanInternalIterator) constructRangeKeyIter() error {
     993           2 :         // We want the bounded iter from iterConfig, but not the collapsing of
     994           2 :         // RangeKeyUnsets and RangeKeyDels.
     995           2 :         i.rangeKey.rangeKeyIter = i.rangeKey.iterConfig.Init(
     996           2 :                 i.comparer, i.seqNum, i.opts.LowerBound, i.opts.UpperBound,
     997           2 :                 nil /* hasPrefix */, nil /* prefix */, true, /* internalKeys */
     998           2 :                 &i.rangeKey.rangeKeyBuffers.internal)
     999           2 : 
    1000           2 :         // Next are the flushables: memtables and large batches.
    1001           2 :         if i.readState != nil {
    1002           2 :                 for j := len(i.readState.memtables) - 1; j >= 0; j-- {
    1003           2 :                         mem := i.readState.memtables[j]
    1004           2 :                         // We only need to read from memtables which contain sequence numbers older
    1005           2 :                         // than seqNum.
    1006           2 :                         if logSeqNum := mem.logSeqNum; logSeqNum >= i.seqNum {
    1007           2 :                                 continue
    1008             :                         }
    1009           2 :                         if rki := mem.newRangeKeyIter(&i.opts.IterOptions); rki != nil {
    1010           2 :                                 i.rangeKey.iterConfig.AddLevel(rki)
    1011           2 :                         }
    1012             :                 }
    1013             :         }
    1014             : 
    1015           2 :         current := i.version
    1016           2 :         if current == nil {
    1017           2 :                 current = i.readState.current
    1018           2 :         }
    1019             :         // Next are the file levels: L0 sub-levels followed by lower levels.
    1020             :         //
    1021             :         // Add file-specific iterators for L0 files containing range keys. This is less
    1022             :         // efficient than using levelIters for sublevels of L0 files containing
    1023             :         // range keys, but range keys are expected to be sparse anyway, reducing the
    1024             :         // cost benefit of maintaining a separate L0Sublevels instance for range key
    1025             :         // files and then using it here.
    1026             :         //
    1027             :         // NB: We iterate L0's files in reverse order. They're sorted by
    1028             :         // LargestSeqNum ascending, and we need to add them to the merging iterator
    1029             :         // in LargestSeqNum descending to preserve the merging iterator's invariants
    1030             :         // around Key InternalKeyTrailer order.
    1031           2 :         iter := current.RangeKeyLevels[0].Iter()
    1032           2 :         for f := iter.Last(); f != nil; f = iter.Prev() {
    1033           2 :                 spanIter, err := i.newIterRangeKey(f, i.opts.SpanIterOptions())
    1034           2 :                 if err != nil {
    1035           0 :                         return err
    1036           0 :                 }
    1037           2 :                 i.rangeKey.iterConfig.AddLevel(spanIter)
    1038             :         }
    1039             :         // Add level iterators for the non-empty non-L0 levels.
    1040           2 :         skipStart := i.opts.skipLevelForOpts()
    1041           2 :         for level := 1; level < len(current.RangeKeyLevels); level++ {
    1042           2 :                 if current.RangeKeyLevels[level].Empty() {
    1043           2 :                         continue
    1044             :                 }
    1045           2 :                 if level > skipStart {
    1046           2 :                         continue
    1047             :                 }
    1048           2 :                 li := i.rangeKey.iterConfig.NewLevelIter()
    1049           2 :                 spanIterOpts := i.opts.SpanIterOptions()
    1050           2 :                 levIter := current.RangeKeyLevels[level].Iter()
    1051           2 :                 if level == skipStart {
    1052           2 :                         nonRemoteFiles := make([]*manifest.FileMetadata, 0)
    1053           2 :                         for f := levIter.First(); f != nil; f = levIter.Next() {
    1054           2 :                                 meta, err := i.db.objProvider.Lookup(fileTypeTable, f.FileBacking.DiskFileNum)
    1055           2 :                                 if err != nil {
    1056           0 :                                         return err
    1057           0 :                                 }
    1058           2 :                                 if (meta.IsShared() && i.opts.visitSharedFile != nil) ||
    1059           2 :                                         (meta.IsExternal() && i.opts.visitExternalFile != nil) {
    1060           2 :                                         // Skip this file.
    1061           2 :                                         continue
    1062             :                                 }
    1063           0 :                                 nonRemoteFiles = append(nonRemoteFiles, f)
    1064             :                         }
    1065           2 :                         levSlice := manifest.NewLevelSliceKeySorted(i.db.cmp, nonRemoteFiles)
    1066           2 :                         levIter = levSlice.Iter()
    1067             :                 }
    1068           2 :                 li.Init(spanIterOpts, i.comparer.Compare, i.newIterRangeKey, levIter,
    1069           2 :                         manifest.Level(level), manifest.KeyTypeRange)
    1070           2 :                 i.rangeKey.iterConfig.AddLevel(li)
    1071             :         }
    1072           2 :         return nil
    1073             : }
    1074             : 
    1075             : // seekGE seeks this iterator to the first key that's greater than or equal
    1076             : // to the specified user key.
    1077           2 : func (i *scanInternalIterator) seekGE(key []byte) bool {
    1078           2 :         i.iterKV = i.iter.SeekGE(key, base.SeekGEFlagsNone)
    1079           2 :         return i.iterKV != nil
    1080           2 : }
    1081             : 
    1082             : // unsafeKey returns the unsafe InternalKey at the current position. The value
    1083             : // is nil if the iterator is invalid or exhausted.
    1084           2 : func (i *scanInternalIterator) unsafeKey() *InternalKey {
    1085           2 :         return &i.iterKV.K
    1086           2 : }
    1087             : 
    1088             : // lazyValue returns a value pointer to the value at the current iterator
    1089             : // position. Behaviour undefined if unsafeKey() returns a Range key or Rangedel
    1090             : // kind key.
    1091           2 : func (i *scanInternalIterator) lazyValue() LazyValue {
    1092           2 :         return i.iterKV.V
    1093           2 : }
    1094             : 
    1095             : // unsafeRangeDel returns a range key span. Behaviour undefined if UnsafeKey returns
    1096             : // a non-rangedel kind.
    1097           2 : func (i *scanInternalIterator) unsafeRangeDel() *keyspan.Span {
    1098           2 :         type spanInternalIterator interface {
    1099           2 :                 Span() *keyspan.Span
    1100           2 :         }
    1101           2 :         return i.pointKeyIter.(spanInternalIterator).Span()
    1102           2 : }
    1103             : 
    1104             : // unsafeSpan returns a range key span. Behaviour undefined if UnsafeKey returns
    1105             : // a non-rangekey type.
    1106           2 : func (i *scanInternalIterator) unsafeSpan() *keyspan.Span {
    1107           2 :         return i.rangeKey.iiter.Span()
    1108           2 : }
    1109             : 
    1110             : // next advances the iterator in the forward direction, and returns the
    1111             : // iterator's new validity state.
    1112           2 : func (i *scanInternalIterator) next() bool {
    1113           2 :         i.iterKV = i.iter.Next()
    1114           2 :         return i.iterKV != nil
    1115           2 : }
    1116             : 
    1117             : // error returns an error from the internal iterator, if there's any.
    1118           2 : func (i *scanInternalIterator) error() error {
    1119           2 :         return i.iter.Error()
    1120           2 : }
    1121             : 
    1122             : // close closes this iterator, and releases any pooled objects.
    1123           2 : func (i *scanInternalIterator) close() error {
    1124           2 :         if err := i.iter.Close(); err != nil {
    1125           0 :                 return err
    1126           0 :         }
    1127           2 :         if i.readState != nil {
    1128           2 :                 i.readState.unref()
    1129           2 :         }
    1130           2 :         if i.version != nil {
    1131           1 :                 i.version.Unref()
    1132           1 :         }
    1133           2 :         if i.rangeKey != nil {
    1134           2 :                 i.rangeKey.PrepareForReuse()
    1135           2 :                 *i.rangeKey = iteratorRangeKeyState{
    1136           2 :                         rangeKeyBuffers: i.rangeKey.rangeKeyBuffers,
    1137           2 :                 }
    1138           2 :                 iterRangeKeyStateAllocPool.Put(i.rangeKey)
    1139           2 :                 i.rangeKey = nil
    1140           2 :         }
    1141           2 :         if alloc := i.alloc; alloc != nil {
    1142           2 :                 for j := range i.boundsBuf {
    1143           2 :                         if cap(i.boundsBuf[j]) >= maxKeyBufCacheSize {
    1144           0 :                                 alloc.boundsBuf[j] = nil
    1145           2 :                         } else {
    1146           2 :                                 alloc.boundsBuf[j] = i.boundsBuf[j]
    1147           2 :                         }
    1148             :                 }
    1149           2 :                 *alloc = iterAlloc{
    1150           2 :                         keyBuf:              alloc.keyBuf[:0],
    1151           2 :                         boundsBuf:           alloc.boundsBuf,
    1152           2 :                         prefixOrFullSeekKey: alloc.prefixOrFullSeekKey[:0],
    1153           2 :                 }
    1154           2 :                 iterAllocPool.Put(alloc)
    1155           2 :                 i.alloc = nil
    1156             :         }
    1157           2 :         return nil
    1158             : }
    1159             : 
    1160           2 : func (i *scanInternalIterator) initializeBoundBufs(lower, upper []byte) {
    1161           2 :         buf := i.boundsBuf[i.boundsBufIdx][:0]
    1162           2 :         if lower != nil {
    1163           2 :                 buf = append(buf, lower...)
    1164           2 :                 i.opts.LowerBound = buf
    1165           2 :         } else {
    1166           1 :                 i.opts.LowerBound = nil
    1167           1 :         }
    1168           2 :         if upper != nil {
    1169           2 :                 buf = append(buf, upper...)
    1170           2 :                 i.opts.UpperBound = buf[len(buf)-len(upper):]
    1171           2 :         } else {
    1172           1 :                 i.opts.UpperBound = nil
    1173           1 :         }
    1174           2 :         i.boundsBuf[i.boundsBufIdx] = buf
    1175           2 :         i.boundsBufIdx = 1 - i.boundsBufIdx
    1176             : }

Generated by: LCOV version 1.14