LCOV - code coverage report
Current view: top level - pebble/sstable - reader_iter_two_lvl.go (source / functions) Hit Total Coverage
Test: 2024-01-06 08:16Z 8ce58a81 - tests only.lcov Lines: 630 788 79.9 %
Date: 2024-01-06 08:18:40 Functions: 0 0 -

          Line data    Source code
       1             : // Copyright 2011 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 sstable
       6             : 
       7             : import (
       8             :         "bytes"
       9             :         "context"
      10             :         "fmt"
      11             : 
      12             :         "github.com/cockroachdb/pebble/internal/base"
      13             :         "github.com/cockroachdb/pebble/objstorage/objstorageprovider/objiotracing"
      14             : )
      15             : 
      16             : type twoLevelIterator struct {
      17             :         singleLevelIterator
      18             :         // maybeFilteredKeysSingleLevel indicates whether the last iterator
      19             :         // positioning operation may have skipped any index blocks due to
      20             :         // block-property filters when positioning the top-level-index.
      21             :         maybeFilteredKeysTwoLevel bool
      22             :         topLevelIndex             blockIter
      23             : }
      24             : 
      25             : // twoLevelIterator implements the base.InternalIterator interface.
      26             : var _ base.InternalIterator = (*twoLevelIterator)(nil)
      27             : 
      28             : // loadIndex loads the index block at the current top level index position and
      29             : // leaves i.index unpositioned. If unsuccessful, it gets i.err to any error
      30             : // encountered, which may be nil if we have simply exhausted the entire table.
      31             : // This is used for two level indexes.
      32           1 : func (i *twoLevelIterator) loadIndex(dir int8) loadBlockResult {
      33           1 :         // Ensure the index data block iterators are invalidated even if loading of
      34           1 :         // the index fails.
      35           1 :         i.data.invalidate()
      36           1 :         i.index.invalidate()
      37           1 :         if !i.topLevelIndex.valid() {
      38           0 :                 i.index.offset = 0
      39           0 :                 i.index.restarts = 0
      40           0 :                 return loadBlockFailed
      41           0 :         }
      42           1 :         v := i.topLevelIndex.value()
      43           1 :         bhp, err := decodeBlockHandleWithProperties(v.InPlaceValue())
      44           1 :         if err != nil {
      45           0 :                 i.err = base.CorruptionErrorf("pebble/table: corrupt top level index entry")
      46           0 :                 return loadBlockFailed
      47           0 :         }
      48           1 :         if i.bpfs != nil {
      49           1 :                 intersects, err := i.bpfs.intersects(bhp.Props)
      50           1 :                 if err != nil {
      51           0 :                         i.err = errCorruptIndexEntry
      52           0 :                         return loadBlockFailed
      53           0 :                 }
      54           1 :                 if intersects == blockMaybeExcluded {
      55           0 :                         intersects = i.resolveMaybeExcluded(dir)
      56           0 :                 }
      57           1 :                 if intersects == blockExcluded {
      58           1 :                         i.maybeFilteredKeysTwoLevel = true
      59           1 :                         return loadBlockIrrelevant
      60           1 :                 }
      61             :                 // blockIntersects
      62             :         }
      63           1 :         ctx := objiotracing.WithBlockType(i.ctx, objiotracing.MetadataBlock)
      64           1 :         indexBlock, err := i.reader.readBlock(
      65           1 :                 ctx, bhp.BlockHandle, nil /* transform */, nil /* readHandle */, i.stats, &i.iterStats, i.bufferPool)
      66           1 :         if err != nil {
      67           1 :                 i.err = err
      68           1 :                 return loadBlockFailed
      69           1 :         }
      70           1 :         if i.err = i.index.initHandle(i.cmp, indexBlock, i.reader.Properties.GlobalSeqNum, false); i.err == nil {
      71           1 :                 return loadBlockOK
      72           1 :         }
      73           0 :         return loadBlockFailed
      74             : }
      75             : 
      76             : // resolveMaybeExcluded is invoked when the block-property filterer has found
      77             : // that an index block is excluded according to its properties but only if its
      78             : // bounds fall within the filter's current bounds. This function consults the
      79             : // apprioriate bound, depending on the iteration direction, and returns either
      80             : // `blockIntersects` or `blockExcluded`.
      81           0 : func (i *twoLevelIterator) resolveMaybeExcluded(dir int8) intersectsResult {
      82           0 :         // This iterator is configured with a bound-limited block property filter.
      83           0 :         // The bpf determined this entire index block could be excluded from
      84           0 :         // iteration based on the property encoded in the block handle. However, we
      85           0 :         // still need to determine if the index block is wholly contained within the
      86           0 :         // filter's key bounds.
      87           0 :         //
      88           0 :         // External guarantees ensure all its data blocks' keys are ≥ the filter's
      89           0 :         // lower bound during forward iteration, and that all its data blocks' keys
      90           0 :         // are < the filter's upper bound during backward iteration. We only need to
      91           0 :         // determine if the opposite bound is also met.
      92           0 :         //
      93           0 :         // The index separator in topLevelIndex.Key() provides an inclusive
      94           0 :         // upper-bound for the index block's keys, guaranteeing that all its keys
      95           0 :         // are ≤ topLevelIndex.Key(). For forward iteration, this is all we need.
      96           0 :         if dir > 0 {
      97           0 :                 // Forward iteration.
      98           0 :                 if i.bpfs.boundLimitedFilter.KeyIsWithinUpperBound(i.topLevelIndex.Key().UserKey) {
      99           0 :                         return blockExcluded
     100           0 :                 }
     101           0 :                 return blockIntersects
     102             :         }
     103             : 
     104             :         // Reverse iteration.
     105             :         //
     106             :         // Because we're iterating in the reverse direction, we don't yet have
     107             :         // enough context available to determine if the block is wholly contained
     108             :         // within its bounds. This case arises only during backward iteration,
     109             :         // because of the way the index is structured.
     110             :         //
     111             :         // Consider a bound-limited bpf limited to the bounds [b,d), loading the
     112             :         // block with separator `c`. During reverse iteration, the guarantee that
     113             :         // all the block's keys are < `d` is externally provided, but no guarantee
     114             :         // is made on the bpf's lower bound. The separator `c` only provides an
     115             :         // inclusive upper bound on the block's keys, indicating that the
     116             :         // corresponding block handle points to a block containing only keys ≤ `c`.
     117             :         //
     118             :         // To establish a lower bound, we step the top-level index backwards to read
     119             :         // the previous block's separator, which provides an inclusive lower bound
     120             :         // on the original index block's keys. Afterwards, we step forward to
     121             :         // restore our top-level index position.
     122           0 :         if peekKey, _ := i.topLevelIndex.Prev(); peekKey == nil {
     123           0 :                 // The original block points to the first index block of this table. If
     124           0 :                 // we knew the lower bound for the entire table, it could provide a
     125           0 :                 // lower bound, but the code refactoring necessary to read it doesn't
     126           0 :                 // seem worth the payoff. We fall through to loading the block.
     127           0 :         } else if i.bpfs.boundLimitedFilter.KeyIsWithinLowerBound(peekKey.UserKey) {
     128           0 :                 // The lower-bound on the original index block falls within the filter's
     129           0 :                 // bounds, and we can skip the block (after restoring our current
     130           0 :                 // top-level index position).
     131           0 :                 _, _ = i.topLevelIndex.Next()
     132           0 :                 return blockExcluded
     133           0 :         }
     134           0 :         _, _ = i.topLevelIndex.Next()
     135           0 :         return blockIntersects
     136             : }
     137             : 
     138             : // Note that lower, upper passed into init has nothing to do with virtual sstable
     139             : // bounds. If the virtualState passed in is not nil, then virtual sstable bounds
     140             : // will be enforced.
     141             : func (i *twoLevelIterator) init(
     142             :         ctx context.Context,
     143             :         r *Reader,
     144             :         v *virtualState,
     145             :         lower, upper []byte,
     146             :         filterer *BlockPropertiesFilterer,
     147             :         useFilter, hideObsoletePoints bool,
     148             :         stats *base.InternalIteratorStats,
     149             :         categoryAndQoS CategoryAndQoS,
     150             :         statsCollector *CategoryStatsCollector,
     151             :         rp ReaderProvider,
     152             :         bufferPool *BufferPool,
     153           1 : ) error {
     154           1 :         if r.err != nil {
     155           0 :                 return r.err
     156           0 :         }
     157           1 :         i.iterStats.init(categoryAndQoS, statsCollector)
     158           1 :         topLevelIndexH, err := r.readIndex(ctx, stats, &i.iterStats)
     159           1 :         if err != nil {
     160           1 :                 return err
     161           1 :         }
     162           1 :         if v != nil {
     163           1 :                 i.vState = v
     164           1 :                 // Note that upper is exclusive here.
     165           1 :                 i.endKeyInclusive, lower, upper = v.constrainBounds(lower, upper, false /* endInclusive */)
     166           1 :         }
     167             : 
     168           1 :         i.ctx = ctx
     169           1 :         i.lower = lower
     170           1 :         i.upper = upper
     171           1 :         i.bpfs = filterer
     172           1 :         i.useFilter = useFilter
     173           1 :         i.reader = r
     174           1 :         i.cmp = r.Compare
     175           1 :         i.stats = stats
     176           1 :         i.hideObsoletePoints = hideObsoletePoints
     177           1 :         i.bufferPool = bufferPool
     178           1 :         err = i.topLevelIndex.initHandle(i.cmp, topLevelIndexH, r.Properties.GlobalSeqNum, false)
     179           1 :         if err != nil {
     180           0 :                 // blockIter.Close releases topLevelIndexH and always returns a nil error
     181           0 :                 _ = i.topLevelIndex.Close()
     182           0 :                 return err
     183           0 :         }
     184           1 :         i.dataRH = r.readable.NewReadHandle(ctx)
     185           1 :         if r.tableFormat >= TableFormatPebblev3 {
     186           1 :                 if r.Properties.NumValueBlocks > 0 {
     187           1 :                         i.vbReader = &valueBlockReader{
     188           1 :                                 bpOpen: i,
     189           1 :                                 rp:     rp,
     190           1 :                                 vbih:   r.valueBIH,
     191           1 :                                 stats:  stats,
     192           1 :                         }
     193           1 :                         i.data.lazyValueHandling.vbr = i.vbReader
     194           1 :                         i.vbRH = r.readable.NewReadHandle(ctx)
     195           1 :                 }
     196           1 :                 i.data.lazyValueHandling.hasValuePrefix = true
     197             :         }
     198           1 :         return nil
     199             : }
     200             : 
     201           0 : func (i *twoLevelIterator) String() string {
     202           0 :         if i.vState != nil {
     203           0 :                 return i.vState.fileNum.String()
     204           0 :         }
     205           0 :         return i.reader.fileNum.String()
     206             : }
     207             : 
     208             : // MaybeFilteredKeys may be called when an iterator is exhausted to indicate
     209             : // whether or not the last positioning method may have skipped any keys due to
     210             : // block-property filters.
     211           1 : func (i *twoLevelIterator) MaybeFilteredKeys() bool {
     212           1 :         // While reading sstables with two-level indexes, knowledge of whether we've
     213           1 :         // filtered keys is tracked separately for each index level. The
     214           1 :         // seek-using-next optimizations have different criteria. We can only reset
     215           1 :         // maybeFilteredKeys back to false during a seek when NOT using the
     216           1 :         // fast-path that uses the current iterator position.
     217           1 :         //
     218           1 :         // If either level might have filtered keys to arrive at the current
     219           1 :         // iterator position, return MaybeFilteredKeys=true.
     220           1 :         return i.maybeFilteredKeysTwoLevel || i.maybeFilteredKeysSingleLevel
     221           1 : }
     222             : 
     223             : // SeekGE implements internalIterator.SeekGE, as documented in the pebble
     224             : // package. Note that SeekGE only checks the upper bound. It is up to the
     225             : // caller to ensure that key is greater than or equal to the lower bound.
     226             : func (i *twoLevelIterator) SeekGE(
     227             :         key []byte, flags base.SeekGEFlags,
     228           1 : ) (*InternalKey, base.LazyValue) {
     229           1 :         if i.vState != nil {
     230           1 :                 // Callers of SeekGE don't know about virtual sstable bounds, so we may
     231           1 :                 // have to internally restrict the bounds.
     232           1 :                 //
     233           1 :                 // TODO(bananabrick): We can optimize away this check for the level iter
     234           1 :                 // if necessary.
     235           1 :                 if i.cmp(key, i.lower) < 0 {
     236           1 :                         key = i.lower
     237           1 :                 }
     238             :         }
     239             : 
     240           1 :         err := i.err
     241           1 :         i.err = nil // clear cached iteration error
     242           1 : 
     243           1 :         // The twoLevelIterator could be already exhausted. Utilize that when
     244           1 :         // trySeekUsingNext is true. See the comment about data-exhausted, PGDE, and
     245           1 :         // bounds-exhausted near the top of the file.
     246           1 :         if flags.TrySeekUsingNext() &&
     247           1 :                 (i.exhaustedBounds == +1 || (i.data.isDataInvalidated() && i.index.isDataInvalidated())) &&
     248           1 :                 err == nil {
     249           1 :                 // Already exhausted, so return nil.
     250           1 :                 return nil, base.LazyValue{}
     251           1 :         }
     252             : 
     253             :         // SeekGE performs various step-instead-of-seeking optimizations: eg enabled
     254             :         // by trySeekUsingNext, or by monotonically increasing bounds (i.boundsCmp).
     255             :         // Care must be taken to ensure that when performing these optimizations and
     256             :         // the iterator becomes exhausted, i.maybeFilteredKeys is set appropriately.
     257             :         // Consider a previous SeekGE that filtered keys from k until the current
     258             :         // iterator position.
     259             :         //
     260             :         // If the previous SeekGE exhausted the iterator while seeking within the
     261             :         // two-level index, it's possible keys greater than or equal to the current
     262             :         // search key were filtered through skipped index blocks. We must not reuse
     263             :         // the position of the two-level index iterator without remembering the
     264             :         // previous value of maybeFilteredKeys.
     265             : 
     266             :         // We fall into the slow path if i.index.isDataInvalidated() even if the
     267             :         // top-level iterator is already positioned correctly and all other
     268             :         // conditions are met. An alternative structure could reuse topLevelIndex's
     269             :         // current position and reload the index block to which it points. Arguably,
     270             :         // an index block load is expensive and the index block may still be earlier
     271             :         // than the index block containing the sought key, resulting in a wasteful
     272             :         // block load.
     273             : 
     274           1 :         var dontSeekWithinSingleLevelIter bool
     275           1 :         if i.topLevelIndex.isDataInvalidated() || !i.topLevelIndex.valid() || i.index.isDataInvalidated() || err != nil ||
     276           1 :                 (i.boundsCmp <= 0 && !flags.TrySeekUsingNext()) || i.cmp(key, i.topLevelIndex.Key().UserKey) > 0 {
     277           1 :                 // Slow-path: need to position the topLevelIndex.
     278           1 : 
     279           1 :                 // The previous exhausted state of singleLevelIterator is no longer
     280           1 :                 // relevant, since we may be moving to a different index block.
     281           1 :                 i.exhaustedBounds = 0
     282           1 :                 i.maybeFilteredKeysTwoLevel = false
     283           1 :                 flags = flags.DisableTrySeekUsingNext()
     284           1 :                 var ikey *InternalKey
     285           1 :                 if ikey, _ = i.topLevelIndex.SeekGE(key, flags); ikey == nil {
     286           1 :                         i.data.invalidate()
     287           1 :                         i.index.invalidate()
     288           1 :                         return nil, base.LazyValue{}
     289           1 :                 }
     290             : 
     291           1 :                 result := i.loadIndex(+1)
     292           1 :                 if result == loadBlockFailed {
     293           1 :                         i.boundsCmp = 0
     294           1 :                         return nil, base.LazyValue{}
     295           1 :                 }
     296           1 :                 if result == loadBlockIrrelevant {
     297           1 :                         // Enforce the upper bound here since don't want to bother moving
     298           1 :                         // to the next entry in the top level index if upper bound is
     299           1 :                         // already exceeded. Note that the next entry starts with keys >=
     300           1 :                         // ikey.UserKey since even though this is the block separator, the
     301           1 :                         // same user key can span multiple index blocks. If upper is
     302           1 :                         // exclusive we use >= below, else we use >.
     303           1 :                         if i.upper != nil {
     304           1 :                                 cmp := i.cmp(ikey.UserKey, i.upper)
     305           1 :                                 if (!i.endKeyInclusive && cmp >= 0) || cmp > 0 {
     306           1 :                                         i.exhaustedBounds = +1
     307           1 :                                 }
     308             :                         }
     309             :                         // Fall through to skipForward.
     310           1 :                         dontSeekWithinSingleLevelIter = true
     311           1 :                         // Clear boundsCmp.
     312           1 :                         //
     313           1 :                         // In the typical cases where dontSeekWithinSingleLevelIter=false,
     314           1 :                         // the singleLevelIterator.SeekGE call will clear boundsCmp.
     315           1 :                         // However, in this case where dontSeekWithinSingleLevelIter=true,
     316           1 :                         // we never seek on the single-level iterator. This call will fall
     317           1 :                         // through to skipForward, which may improperly leave boundsCmp=+1
     318           1 :                         // unless we clear it here.
     319           1 :                         i.boundsCmp = 0
     320             :                 }
     321           1 :         } else {
     322           1 :                 // INVARIANT: err == nil.
     323           1 :                 //
     324           1 :                 // Else fast-path: There are two possible cases, from
     325           1 :                 // (i.boundsCmp > 0 || flags.TrySeekUsingNext()):
     326           1 :                 //
     327           1 :                 // 1) The bounds have moved forward (i.boundsCmp > 0) and this SeekGE is
     328           1 :                 // respecting the lower bound (guaranteed by Iterator). We know that the
     329           1 :                 // iterator must already be positioned within or just outside the previous
     330           1 :                 // bounds. Therefore, the topLevelIndex iter cannot be positioned at an
     331           1 :                 // entry ahead of the seek position (though it can be positioned behind).
     332           1 :                 // The !i.cmp(key, i.topLevelIndex.Key().UserKey) > 0 confirms that it is
     333           1 :                 // not behind. Since it is not ahead and not behind it must be at the
     334           1 :                 // right position.
     335           1 :                 //
     336           1 :                 // 2) This SeekGE will land on a key that is greater than the key we are
     337           1 :                 // currently at (guaranteed by trySeekUsingNext), but since i.cmp(key,
     338           1 :                 // i.topLevelIndex.Key().UserKey) <= 0, we are at the correct lower level
     339           1 :                 // index block. No need to reset the state of singleLevelIterator.
     340           1 :                 //
     341           1 :                 // Note that cases 1 and 2 never overlap, and one of them must be true,
     342           1 :                 // but we have some test code (TestIterRandomizedMaybeFilteredKeys) that
     343           1 :                 // sets both to true, so we fix things here and then do an invariant
     344           1 :                 // check.
     345           1 :                 //
     346           1 :                 // This invariant checking is important enough that we do not gate it
     347           1 :                 // behind invariants.Enabled.
     348           1 :                 if i.boundsCmp > 0 {
     349           1 :                         // TODO(sumeer): fix TestIterRandomizedMaybeFilteredKeys so as to not
     350           1 :                         // need this behavior.
     351           1 :                         flags = flags.DisableTrySeekUsingNext()
     352           1 :                 }
     353           1 :                 if i.boundsCmp > 0 == flags.TrySeekUsingNext() {
     354           0 :                         panic(fmt.Sprintf("inconsistency in optimization case 1 %t and case 2 %t",
     355           0 :                                 i.boundsCmp > 0, flags.TrySeekUsingNext()))
     356             :                 }
     357             : 
     358           1 :                 if !flags.TrySeekUsingNext() {
     359           1 :                         // Case 1. Bounds have changed so the previous exhausted bounds state is
     360           1 :                         // irrelevant.
     361           1 :                         // WARNING-data-exhausted: this is safe to do only because the monotonic
     362           1 :                         // bounds optimizations only work when !data-exhausted. If they also
     363           1 :                         // worked with data-exhausted, we have made it unclear whether
     364           1 :                         // data-exhausted is actually true. See the comment at the top of the
     365           1 :                         // file.
     366           1 :                         i.exhaustedBounds = 0
     367           1 :                 }
     368             :                 // Else flags.TrySeekUsingNext(). The i.exhaustedBounds is important to
     369             :                 // preserve for singleLevelIterator, and twoLevelIterator.skipForward. See
     370             :                 // bug https://github.com/cockroachdb/pebble/issues/2036.
     371             :         }
     372             : 
     373           1 :         if !dontSeekWithinSingleLevelIter {
     374           1 :                 // Note that while trySeekUsingNext could be false here, singleLevelIterator
     375           1 :                 // could do its own boundsCmp-based optimization to seek using next.
     376           1 :                 if ikey, val := i.singleLevelIterator.SeekGE(key, flags); ikey != nil {
     377           1 :                         return ikey, val
     378           1 :                 }
     379             :         }
     380           1 :         return i.skipForward()
     381             : }
     382             : 
     383             : // SeekPrefixGE implements internalIterator.SeekPrefixGE, as documented in the
     384             : // pebble package. Note that SeekPrefixGE only checks the upper bound. It is up
     385             : // to the caller to ensure that key is greater than or equal to the lower bound.
     386             : func (i *twoLevelIterator) SeekPrefixGE(
     387             :         prefix, key []byte, flags base.SeekGEFlags,
     388           1 : ) (*base.InternalKey, base.LazyValue) {
     389           1 :         if i.vState != nil {
     390           1 :                 // Callers of SeekGE don't know about virtual sstable bounds, so we may
     391           1 :                 // have to internally restrict the bounds.
     392           1 :                 //
     393           1 :                 // TODO(bananabrick): We can optimize away this check for the level iter
     394           1 :                 // if necessary.
     395           1 :                 if i.cmp(key, i.lower) < 0 {
     396           1 :                         key = i.lower
     397           1 :                 }
     398             :         }
     399             : 
     400             :         // NOTE: prefix is only used for bloom filter checking and not later work in
     401             :         // this method. Hence, we can use the existing iterator position if the last
     402             :         // SeekPrefixGE did not fail bloom filter matching.
     403             : 
     404           1 :         err := i.err
     405           1 :         i.err = nil // clear cached iteration error
     406           1 : 
     407           1 :         // The twoLevelIterator could be already exhausted. Utilize that when
     408           1 :         // trySeekUsingNext is true. See the comment about data-exhausted, PGDE, and
     409           1 :         // bounds-exhausted near the top of the file.
     410           1 :         filterUsedAndDidNotMatch :=
     411           1 :                 i.reader.tableFilter != nil && i.useFilter && !i.lastBloomFilterMatched
     412           1 :         if flags.TrySeekUsingNext() && !filterUsedAndDidNotMatch &&
     413           1 :                 (i.exhaustedBounds == +1 || (i.data.isDataInvalidated() && i.index.isDataInvalidated())) &&
     414           1 :                 err == nil {
     415           0 :                 // Already exhausted, so return nil.
     416           0 :                 return nil, base.LazyValue{}
     417           0 :         }
     418             : 
     419             :         // Check prefix bloom filter.
     420           1 :         if i.reader.tableFilter != nil && i.useFilter {
     421           1 :                 if !i.lastBloomFilterMatched {
     422           1 :                         // Iterator is not positioned based on last seek.
     423           1 :                         flags = flags.DisableTrySeekUsingNext()
     424           1 :                 }
     425           1 :                 i.lastBloomFilterMatched = false
     426           1 :                 var dataH bufferHandle
     427           1 :                 dataH, i.err = i.reader.readFilter(i.ctx, i.stats, &i.iterStats)
     428           1 :                 if i.err != nil {
     429           1 :                         i.data.invalidate()
     430           1 :                         return nil, base.LazyValue{}
     431           1 :                 }
     432           1 :                 mayContain := i.reader.tableFilter.mayContain(dataH.Get(), prefix)
     433           1 :                 dataH.Release()
     434           1 :                 if !mayContain {
     435           1 :                         // This invalidation may not be necessary for correctness, and may
     436           1 :                         // be a place to optimize later by reusing the already loaded
     437           1 :                         // block. It was necessary in earlier versions of the code since
     438           1 :                         // the caller was allowed to call Next when SeekPrefixGE returned
     439           1 :                         // nil. This is no longer allowed.
     440           1 :                         i.data.invalidate()
     441           1 :                         return nil, base.LazyValue{}
     442           1 :                 }
     443           1 :                 i.lastBloomFilterMatched = true
     444             :         }
     445             : 
     446             :         // Bloom filter matches.
     447             : 
     448             :         // SeekPrefixGE performs various step-instead-of-seeking optimizations: eg
     449             :         // enabled by trySeekUsingNext, or by monotonically increasing bounds
     450             :         // (i.boundsCmp).  Care must be taken to ensure that when performing these
     451             :         // optimizations and the iterator becomes exhausted,
     452             :         // i.maybeFilteredKeysTwoLevel is set appropriately.  Consider a previous
     453             :         // SeekPrefixGE that filtered keys from k until the current iterator
     454             :         // position.
     455             :         //
     456             :         // If the previous SeekPrefixGE exhausted the iterator while seeking within
     457             :         // the two-level index, it's possible keys greater than or equal to the
     458             :         // current search key were filtered through skipped index blocks. We must
     459             :         // not reuse the position of the two-level index iterator without
     460             :         // remembering the previous value of maybeFilteredKeysTwoLevel.
     461             : 
     462             :         // We fall into the slow path if i.index.isDataInvalidated() even if the
     463             :         // top-level iterator is already positioned correctly and all other
     464             :         // conditions are met. An alternative structure could reuse topLevelIndex's
     465             :         // current position and reload the index block to which it points. Arguably,
     466             :         // an index block load is expensive and the index block may still be earlier
     467             :         // than the index block containing the sought key, resulting in a wasteful
     468             :         // block load.
     469             : 
     470           1 :         var dontSeekWithinSingleLevelIter bool
     471           1 :         if i.topLevelIndex.isDataInvalidated() || !i.topLevelIndex.valid() || i.index.isDataInvalidated() || err != nil ||
     472           1 :                 (i.boundsCmp <= 0 && !flags.TrySeekUsingNext()) || i.cmp(key, i.topLevelIndex.Key().UserKey) > 0 {
     473           1 :                 // Slow-path: need to position the topLevelIndex.
     474           1 : 
     475           1 :                 // The previous exhausted state of singleLevelIterator is no longer
     476           1 :                 // relevant, since we may be moving to a different index block.
     477           1 :                 i.exhaustedBounds = 0
     478           1 :                 i.maybeFilteredKeysTwoLevel = false
     479           1 :                 flags = flags.DisableTrySeekUsingNext()
     480           1 :                 var ikey *InternalKey
     481           1 :                 if ikey, _ = i.topLevelIndex.SeekGE(key, flags); ikey == nil {
     482           1 :                         i.data.invalidate()
     483           1 :                         i.index.invalidate()
     484           1 :                         return nil, base.LazyValue{}
     485           1 :                 }
     486             : 
     487           1 :                 result := i.loadIndex(+1)
     488           1 :                 if result == loadBlockFailed {
     489           1 :                         i.boundsCmp = 0
     490           1 :                         return nil, base.LazyValue{}
     491           1 :                 }
     492           1 :                 if result == loadBlockIrrelevant {
     493           1 :                         // Enforce the upper bound here since don't want to bother moving
     494           1 :                         // to the next entry in the top level index if upper bound is
     495           1 :                         // already exceeded. Note that the next entry starts with keys >=
     496           1 :                         // ikey.UserKey since even though this is the block separator, the
     497           1 :                         // same user key can span multiple index blocks. If upper is
     498           1 :                         // exclusive we use >= below, else we use >.
     499           1 :                         if i.upper != nil {
     500           1 :                                 cmp := i.cmp(ikey.UserKey, i.upper)
     501           1 :                                 if (!i.endKeyInclusive && cmp >= 0) || cmp > 0 {
     502           1 :                                         i.exhaustedBounds = +1
     503           1 :                                 }
     504             :                         }
     505             :                         // Fall through to skipForward.
     506           1 :                         dontSeekWithinSingleLevelIter = true
     507           1 :                         // Clear boundsCmp.
     508           1 :                         //
     509           1 :                         // In the typical cases where dontSeekWithinSingleLevelIter=false,
     510           1 :                         // the singleLevelIterator.SeekPrefixGE call will clear boundsCmp.
     511           1 :                         // However, in this case where dontSeekWithinSingleLevelIter=true,
     512           1 :                         // we never seek on the single-level iterator. This call will fall
     513           1 :                         // through to skipForward, which may improperly leave boundsCmp=+1
     514           1 :                         // unless we clear it here.
     515           1 :                         i.boundsCmp = 0
     516             :                 }
     517           1 :         } else {
     518           1 :                 // INVARIANT: err == nil.
     519           1 :                 //
     520           1 :                 // Else fast-path: There are two possible cases, from
     521           1 :                 // (i.boundsCmp > 0 || flags.TrySeekUsingNext()):
     522           1 :                 //
     523           1 :                 // 1) The bounds have moved forward (i.boundsCmp > 0) and this
     524           1 :                 // SeekPrefixGE is respecting the lower bound (guaranteed by Iterator). We
     525           1 :                 // know that the iterator must already be positioned within or just
     526           1 :                 // outside the previous bounds. Therefore, the topLevelIndex iter cannot
     527           1 :                 // be positioned at an entry ahead of the seek position (though it can be
     528           1 :                 // positioned behind). The !i.cmp(key, i.topLevelIndex.Key().UserKey) > 0
     529           1 :                 // confirms that it is not behind. Since it is not ahead and not behind it
     530           1 :                 // must be at the right position.
     531           1 :                 //
     532           1 :                 // 2) This SeekPrefixGE will land on a key that is greater than the key we
     533           1 :                 // are currently at (guaranteed by trySeekUsingNext), but since i.cmp(key,
     534           1 :                 // i.topLevelIndex.Key().UserKey) <= 0, we are at the correct lower level
     535           1 :                 // index block. No need to reset the state of singleLevelIterator.
     536           1 :                 //
     537           1 :                 // Note that cases 1 and 2 never overlap, and one of them must be true.
     538           1 :                 // This invariant checking is important enough that we do not gate it
     539           1 :                 // behind invariants.Enabled.
     540           1 :                 if i.boundsCmp > 0 == flags.TrySeekUsingNext() {
     541           0 :                         panic(fmt.Sprintf("inconsistency in optimization case 1 %t and case 2 %t",
     542           0 :                                 i.boundsCmp > 0, flags.TrySeekUsingNext()))
     543             :                 }
     544             : 
     545           1 :                 if !flags.TrySeekUsingNext() {
     546           0 :                         // Case 1. Bounds have changed so the previous exhausted bounds state is
     547           0 :                         // irrelevant.
     548           0 :                         // WARNING-data-exhausted: this is safe to do only because the monotonic
     549           0 :                         // bounds optimizations only work when !data-exhausted. If they also
     550           0 :                         // worked with data-exhausted, we have made it unclear whether
     551           0 :                         // data-exhausted is actually true. See the comment at the top of the
     552           0 :                         // file.
     553           0 :                         i.exhaustedBounds = 0
     554           0 :                 }
     555             :                 // Else flags.TrySeekUsingNext(). The i.exhaustedBounds is important to
     556             :                 // preserve for singleLevelIterator, and twoLevelIterator.skipForward. See
     557             :                 // bug https://github.com/cockroachdb/pebble/issues/2036.
     558             :         }
     559             : 
     560           1 :         if !dontSeekWithinSingleLevelIter {
     561           1 :                 if ikey, val := i.singleLevelIterator.seekPrefixGE(
     562           1 :                         prefix, key, flags, false /* checkFilter */); ikey != nil {
     563           1 :                         return ikey, val
     564           1 :                 }
     565             :         }
     566             :         // NB: skipForward checks whether exhaustedBounds is already +1.
     567           1 :         return i.skipForward()
     568             : }
     569             : 
     570             : // virtualLast should only be called if i.vReader != nil.
     571           1 : func (i *twoLevelIterator) virtualLast() (*InternalKey, base.LazyValue) {
     572           1 :         if i.vState == nil {
     573           0 :                 panic("pebble: invalid call to virtualLast")
     574             :         }
     575           1 :         if !i.endKeyInclusive {
     576           1 :                 // Trivial case.
     577           1 :                 return i.SeekLT(i.upper, base.SeekLTFlagsNone)
     578           1 :         }
     579           1 :         return i.virtualLastSeekLE(i.upper)
     580             : }
     581             : 
     582             : // virtualLastSeekLE implements a SeekLE() that can be used as part
     583             : // of reverse-iteration calls such as a Last() on a virtual sstable.
     584           1 : func (i *twoLevelIterator) virtualLastSeekLE(key []byte) (*InternalKey, base.LazyValue) {
     585           1 :         // Callers of SeekLE don't know about virtual sstable bounds, so we may
     586           1 :         // have to internally restrict the bounds.
     587           1 :         //
     588           1 :         // TODO(bananabrick): We can optimize this check away for the level iter
     589           1 :         // if necessary.
     590           1 :         if i.cmp(key, i.upper) >= 0 {
     591           1 :                 if !i.endKeyInclusive {
     592           0 :                         panic("unexpected virtualLastSeekLE with exclusive upper bounds")
     593             :                 }
     594           1 :                 key = i.upper
     595             :         }
     596             :         // Need to position the topLevelIndex.
     597             :         //
     598             :         // The previous exhausted state of singleLevelIterator is no longer
     599             :         // relevant, since we may be moving to a different index block.
     600           1 :         i.exhaustedBounds = 0
     601           1 :         // Seek optimization only applies until iterator is first positioned with a
     602           1 :         // SeekGE or SeekLT after SetBounds.
     603           1 :         i.boundsCmp = 0
     604           1 :         i.maybeFilteredKeysTwoLevel = false
     605           1 :         ikey, _ := i.topLevelIndex.SeekGE(key, base.SeekGEFlagsNone)
     606           1 :         // We can have multiple internal keys with the same user key as the seek
     607           1 :         // key. In that case, we want the last (greatest) internal key.
     608           1 :         for ikey != nil && bytes.Equal(ikey.UserKey, key) {
     609           1 :                 ikey, _ = i.topLevelIndex.Next()
     610           1 :         }
     611           1 :         if ikey == nil {
     612           0 :                 return i.skipBackward()
     613           0 :         }
     614           1 :         result := i.loadIndex(-1)
     615           1 :         if result == loadBlockFailed {
     616           0 :                 i.boundsCmp = 0
     617           0 :                 return nil, base.LazyValue{}
     618           0 :         }
     619           1 :         if result == loadBlockIrrelevant {
     620           0 :                 // Load the previous block.
     621           0 :                 return i.skipBackward()
     622           0 :         }
     623           1 :         if ikey, val := i.singleLevelIterator.virtualLastSeekLE(key); ikey != nil {
     624           1 :                 return ikey, val
     625           1 :         }
     626           1 :         return i.skipBackward()
     627             : }
     628             : 
     629             : // SeekLT implements internalIterator.SeekLT, as documented in the pebble
     630             : // package. Note that SeekLT only checks the lower bound. It is up to the
     631             : // caller to ensure that key is less than the upper bound.
     632             : func (i *twoLevelIterator) SeekLT(
     633             :         key []byte, flags base.SeekLTFlags,
     634           1 : ) (*InternalKey, base.LazyValue) {
     635           1 :         if i.vState != nil {
     636           1 :                 // Might have to fix upper bound since virtual sstable bounds are not
     637           1 :                 // known to callers of SeekLT.
     638           1 :                 //
     639           1 :                 // TODO(bananabrick): We can optimize away this check for the level iter
     640           1 :                 // if necessary.
     641           1 :                 cmp := i.cmp(key, i.upper)
     642           1 :                 // key == i.upper is fine. We'll do the right thing and return the
     643           1 :                 // first internal key with user key < key.
     644           1 :                 if cmp > 0 {
     645           1 :                         return i.virtualLast()
     646           1 :                 }
     647             :         }
     648             : 
     649           1 :         i.exhaustedBounds = 0
     650           1 :         i.err = nil // clear cached iteration error
     651           1 :         // Seek optimization only applies until iterator is first positioned after SetBounds.
     652           1 :         i.boundsCmp = 0
     653           1 : 
     654           1 :         var result loadBlockResult
     655           1 :         var ikey *InternalKey
     656           1 :         // NB: Unlike SeekGE, we don't have a fast-path here since we don't know
     657           1 :         // whether the topLevelIndex is positioned after the position that would
     658           1 :         // be returned by doing i.topLevelIndex.SeekGE(). To know this we would
     659           1 :         // need to know the index key preceding the current one.
     660           1 :         // NB: If a bound-limited block property filter is configured, it's
     661           1 :         // externally ensured that the filter is disabled (through returning
     662           1 :         // Intersects=false irrespective of the block props provided) during seeks.
     663           1 :         i.maybeFilteredKeysTwoLevel = false
     664           1 :         if ikey, _ = i.topLevelIndex.SeekGE(key, base.SeekGEFlagsNone); ikey == nil {
     665           1 :                 if ikey, _ = i.topLevelIndex.Last(); ikey == nil {
     666           0 :                         i.data.invalidate()
     667           0 :                         i.index.invalidate()
     668           0 :                         return nil, base.LazyValue{}
     669           0 :                 }
     670             : 
     671           1 :                 result = i.loadIndex(-1)
     672           1 :                 if result == loadBlockFailed {
     673           1 :                         return nil, base.LazyValue{}
     674           1 :                 }
     675           1 :                 if result == loadBlockOK {
     676           1 :                         if ikey, val := i.singleLevelIterator.lastInternal(); ikey != nil {
     677           1 :                                 return i.maybeVerifyKey(ikey, val)
     678           1 :                         }
     679             :                         // Fall through to skipBackward since the singleLevelIterator did
     680             :                         // not have any blocks that satisfy the block interval
     681             :                         // constraints, or the lower bound was reached.
     682             :                 }
     683             :                 // Else loadBlockIrrelevant, so fall through.
     684           1 :         } else {
     685           1 :                 result = i.loadIndex(-1)
     686           1 :                 if result == loadBlockFailed {
     687           1 :                         return nil, base.LazyValue{}
     688           1 :                 }
     689           1 :                 if result == loadBlockOK {
     690           1 :                         if ikey, val := i.singleLevelIterator.SeekLT(key, flags); ikey != nil {
     691           1 :                                 return i.maybeVerifyKey(ikey, val)
     692           1 :                         }
     693             :                         // Fall through to skipBackward since the singleLevelIterator did
     694             :                         // not have any blocks that satisfy the block interval
     695             :                         // constraint, or the lower bound was reached.
     696             :                 }
     697             :                 // Else loadBlockIrrelevant, so fall through.
     698             :         }
     699           1 :         if result == loadBlockIrrelevant {
     700           1 :                 // Enforce the lower bound here since don't want to bother moving to
     701           1 :                 // the previous entry in the top level index if lower bound is already
     702           1 :                 // exceeded. Note that the previous entry starts with keys <=
     703           1 :                 // ikey.UserKey since even though this is the current block's
     704           1 :                 // separator, the same user key can span multiple index blocks.
     705           1 :                 if i.lower != nil && i.cmp(ikey.UserKey, i.lower) < 0 {
     706           0 :                         i.exhaustedBounds = -1
     707           0 :                 }
     708             :         }
     709             :         // NB: skipBackward checks whether exhaustedBounds is already -1.
     710           1 :         return i.skipBackward()
     711             : }
     712             : 
     713             : // First implements internalIterator.First, as documented in the pebble
     714             : // package. Note that First only checks the upper bound. It is up to the caller
     715             : // to ensure that key is greater than or equal to the lower bound (e.g. via a
     716             : // call to SeekGE(lower)).
     717           1 : func (i *twoLevelIterator) First() (*InternalKey, base.LazyValue) {
     718           1 :         // If the iterator was created on a virtual sstable, we will SeekGE to the
     719           1 :         // lower bound instead of using First, because First does not respect
     720           1 :         // bounds.
     721           1 :         if i.vState != nil {
     722           1 :                 return i.SeekGE(i.lower, base.SeekGEFlagsNone)
     723           1 :         }
     724             : 
     725           1 :         if i.lower != nil {
     726           0 :                 panic("twoLevelIterator.First() used despite lower bound")
     727             :         }
     728           1 :         i.exhaustedBounds = 0
     729           1 :         i.maybeFilteredKeysTwoLevel = false
     730           1 :         i.err = nil // clear cached iteration error
     731           1 :         // Seek optimization only applies until iterator is first positioned after SetBounds.
     732           1 :         i.boundsCmp = 0
     733           1 : 
     734           1 :         var ikey *InternalKey
     735           1 :         if ikey, _ = i.topLevelIndex.First(); ikey == nil {
     736           0 :                 return nil, base.LazyValue{}
     737           0 :         }
     738             : 
     739           1 :         result := i.loadIndex(+1)
     740           1 :         if result == loadBlockFailed {
     741           1 :                 return nil, base.LazyValue{}
     742           1 :         }
     743           1 :         if result == loadBlockOK {
     744           1 :                 if ikey, val := i.singleLevelIterator.First(); ikey != nil {
     745           1 :                         return ikey, val
     746           1 :                 }
     747             :                 // Else fall through to skipForward.
     748           1 :         } else {
     749           1 :                 // result == loadBlockIrrelevant. Enforce the upper bound here since
     750           1 :                 // don't want to bother moving to the next entry in the top level
     751           1 :                 // index if upper bound is already exceeded. Note that the next entry
     752           1 :                 // starts with keys >= ikey.UserKey since even though this is the
     753           1 :                 // block separator, the same user key can span multiple index blocks.
     754           1 :                 // If upper is exclusive we use >= below, else we use >.
     755           1 :                 if i.upper != nil {
     756           0 :                         cmp := i.cmp(ikey.UserKey, i.upper)
     757           0 :                         if (!i.endKeyInclusive && cmp >= 0) || cmp > 0 {
     758           0 :                                 i.exhaustedBounds = +1
     759           0 :                         }
     760             :                 }
     761             :         }
     762             :         // NB: skipForward checks whether exhaustedBounds is already +1.
     763           1 :         return i.skipForward()
     764             : }
     765             : 
     766             : // Last implements internalIterator.Last, as documented in the pebble
     767             : // package. Note that Last only checks the lower bound. It is up to the caller
     768             : // to ensure that key is less than the upper bound (e.g. via a call to
     769             : // SeekLT(upper))
     770           1 : func (i *twoLevelIterator) Last() (*InternalKey, base.LazyValue) {
     771           1 :         if i.vState != nil {
     772           1 :                 if i.endKeyInclusive {
     773           1 :                         return i.virtualLast()
     774           1 :                 }
     775           1 :                 return i.SeekLT(i.upper, base.SeekLTFlagsNone)
     776             :         }
     777             : 
     778           1 :         if i.upper != nil {
     779           0 :                 panic("twoLevelIterator.Last() used despite upper bound")
     780             :         }
     781           1 :         i.exhaustedBounds = 0
     782           1 :         i.maybeFilteredKeysTwoLevel = false
     783           1 :         i.err = nil // clear cached iteration error
     784           1 :         // Seek optimization only applies until iterator is first positioned after SetBounds.
     785           1 :         i.boundsCmp = 0
     786           1 : 
     787           1 :         var ikey *InternalKey
     788           1 :         if ikey, _ = i.topLevelIndex.Last(); ikey == nil {
     789           0 :                 return nil, base.LazyValue{}
     790           0 :         }
     791             : 
     792           1 :         result := i.loadIndex(-1)
     793           1 :         if result == loadBlockFailed {
     794           1 :                 return nil, base.LazyValue{}
     795           1 :         }
     796           1 :         if result == loadBlockOK {
     797           1 :                 if ikey, val := i.singleLevelIterator.Last(); ikey != nil {
     798           1 :                         return ikey, val
     799           1 :                 }
     800             :                 // Else fall through to skipBackward.
     801           1 :         } else {
     802           1 :                 // result == loadBlockIrrelevant. Enforce the lower bound here
     803           1 :                 // since don't want to bother moving to the previous entry in the
     804           1 :                 // top level index if lower bound is already exceeded. Note that
     805           1 :                 // the previous entry starts with keys <= ikey.UserKey since even
     806           1 :                 // though this is the current block's separator, the same user key
     807           1 :                 // can span multiple index blocks.
     808           1 :                 if i.lower != nil && i.cmp(ikey.UserKey, i.lower) < 0 {
     809           0 :                         i.exhaustedBounds = -1
     810           0 :                 }
     811             :         }
     812             :         // NB: skipBackward checks whether exhaustedBounds is already -1.
     813           1 :         return i.skipBackward()
     814             : }
     815             : 
     816             : // Next implements internalIterator.Next, as documented in the pebble
     817             : // package.
     818             : // Note: twoLevelCompactionIterator.Next mirrors the implementation of
     819             : // twoLevelIterator.Next due to performance. Keep the two in sync.
     820           1 : func (i *twoLevelIterator) Next() (*InternalKey, base.LazyValue) {
     821           1 :         // Seek optimization only applies until iterator is first positioned after SetBounds.
     822           1 :         i.boundsCmp = 0
     823           1 :         i.maybeFilteredKeysTwoLevel = false
     824           1 :         if i.err != nil {
     825           1 :                 // TODO(jackson): Can this case be turned into a panic? Once an error is
     826           1 :                 // encountered, the iterator must be re-seeked.
     827           1 :                 return nil, base.LazyValue{}
     828           1 :         }
     829           1 :         if key, val := i.singleLevelIterator.Next(); key != nil {
     830           1 :                 return key, val
     831           1 :         }
     832           1 :         return i.skipForward()
     833             : }
     834             : 
     835             : // NextPrefix implements (base.InternalIterator).NextPrefix.
     836           1 : func (i *twoLevelIterator) NextPrefix(succKey []byte) (*InternalKey, base.LazyValue) {
     837           1 :         if i.exhaustedBounds == +1 {
     838           0 :                 panic("Next called even though exhausted upper bound")
     839             :         }
     840             :         // Seek optimization only applies until iterator is first positioned after SetBounds.
     841           1 :         i.boundsCmp = 0
     842           1 :         i.maybeFilteredKeysTwoLevel = false
     843           1 :         if i.err != nil {
     844           0 :                 // TODO(jackson): Can this case be turned into a panic? Once an error is
     845           0 :                 // encountered, the iterator must be re-seeked.
     846           0 :                 return nil, base.LazyValue{}
     847           0 :         }
     848           1 :         if key, val := i.singleLevelIterator.NextPrefix(succKey); key != nil {
     849           1 :                 return key, val
     850           1 :         }
     851             :         // key == nil
     852           1 :         if i.err != nil {
     853           1 :                 return nil, base.LazyValue{}
     854           1 :         }
     855             : 
     856             :         // Did not find prefix in the existing second-level index block. This is the
     857             :         // slow-path where we seek the iterator.
     858           1 :         var ikey *InternalKey
     859           1 :         if ikey, _ = i.topLevelIndex.SeekGE(succKey, base.SeekGEFlagsNone); ikey == nil {
     860           0 :                 i.data.invalidate()
     861           0 :                 i.index.invalidate()
     862           0 :                 return nil, base.LazyValue{}
     863           0 :         }
     864           1 :         result := i.loadIndex(+1)
     865           1 :         if result == loadBlockFailed {
     866           1 :                 return nil, base.LazyValue{}
     867           1 :         }
     868           1 :         if result == loadBlockIrrelevant {
     869           0 :                 // Enforce the upper bound here since don't want to bother moving to the
     870           0 :                 // next entry in the top level index if upper bound is already exceeded.
     871           0 :                 // Note that the next entry starts with keys >= ikey.UserKey since even
     872           0 :                 // though this is the block separator, the same user key can span multiple
     873           0 :                 // index blocks. If upper is exclusive we use >= below, else we use >.
     874           0 :                 if i.upper != nil {
     875           0 :                         cmp := i.cmp(ikey.UserKey, i.upper)
     876           0 :                         if (!i.endKeyInclusive && cmp >= 0) || cmp > 0 {
     877           0 :                                 i.exhaustedBounds = +1
     878           0 :                         }
     879             :                 }
     880           1 :         } else if key, val := i.singleLevelIterator.SeekGE(succKey, base.SeekGEFlagsNone); key != nil {
     881           1 :                 return i.maybeVerifyKey(key, val)
     882           1 :         }
     883           1 :         return i.skipForward()
     884             : }
     885             : 
     886             : // Prev implements internalIterator.Prev, as documented in the pebble
     887             : // package.
     888           1 : func (i *twoLevelIterator) Prev() (*InternalKey, base.LazyValue) {
     889           1 :         // Seek optimization only applies until iterator is first positioned after SetBounds.
     890           1 :         i.boundsCmp = 0
     891           1 :         i.maybeFilteredKeysTwoLevel = false
     892           1 :         if i.err != nil {
     893           1 :                 return nil, base.LazyValue{}
     894           1 :         }
     895           1 :         if key, val := i.singleLevelIterator.Prev(); key != nil {
     896           1 :                 return key, val
     897           1 :         }
     898           1 :         return i.skipBackward()
     899             : }
     900             : 
     901           1 : func (i *twoLevelIterator) skipForward() (*InternalKey, base.LazyValue) {
     902           1 :         for {
     903           1 :                 if i.err != nil || i.exhaustedBounds > 0 {
     904           1 :                         return nil, base.LazyValue{}
     905           1 :                 }
     906           1 :                 i.exhaustedBounds = 0
     907           1 :                 var ikey *InternalKey
     908           1 :                 if ikey, _ = i.topLevelIndex.Next(); ikey == nil {
     909           1 :                         i.data.invalidate()
     910           1 :                         i.index.invalidate()
     911           1 :                         return nil, base.LazyValue{}
     912           1 :                 }
     913           1 :                 result := i.loadIndex(+1)
     914           1 :                 if result == loadBlockFailed {
     915           1 :                         return nil, base.LazyValue{}
     916           1 :                 }
     917           1 :                 if result == loadBlockOK {
     918           1 :                         if ikey, val := i.singleLevelIterator.firstInternal(); ikey != nil {
     919           1 :                                 return i.maybeVerifyKey(ikey, val)
     920           1 :                         }
     921             :                         // Next iteration will return if singleLevelIterator set
     922             :                         // exhaustedBounds = +1.
     923           1 :                 } else {
     924           1 :                         // result == loadBlockIrrelevant. Enforce the upper bound here
     925           1 :                         // since don't want to bother moving to the next entry in the top
     926           1 :                         // level index if upper bound is already exceeded. Note that the
     927           1 :                         // next entry starts with keys >= ikey.UserKey since even though
     928           1 :                         // this is the block separator, the same user key can span
     929           1 :                         // multiple index blocks. If upper is exclusive we use >=
     930           1 :                         // below, else we use >.
     931           1 :                         if i.upper != nil {
     932           1 :                                 cmp := i.cmp(ikey.UserKey, i.upper)
     933           1 :                                 if (!i.endKeyInclusive && cmp >= 0) || cmp > 0 {
     934           1 :                                         i.exhaustedBounds = +1
     935           1 :                                         // Next iteration will return.
     936           1 :                                 }
     937             :                         }
     938             :                 }
     939             :         }
     940             : }
     941             : 
     942           1 : func (i *twoLevelIterator) skipBackward() (*InternalKey, base.LazyValue) {
     943           1 :         for {
     944           1 :                 if i.err != nil || i.exhaustedBounds < 0 {
     945           1 :                         return nil, base.LazyValue{}
     946           1 :                 }
     947           1 :                 i.exhaustedBounds = 0
     948           1 :                 var ikey *InternalKey
     949           1 :                 if ikey, _ = i.topLevelIndex.Prev(); ikey == nil {
     950           1 :                         i.data.invalidate()
     951           1 :                         i.index.invalidate()
     952           1 :                         return nil, base.LazyValue{}
     953           1 :                 }
     954           1 :                 result := i.loadIndex(-1)
     955           1 :                 if result == loadBlockFailed {
     956           1 :                         return nil, base.LazyValue{}
     957           1 :                 }
     958           1 :                 if result == loadBlockOK {
     959           1 :                         if ikey, val := i.singleLevelIterator.lastInternal(); ikey != nil {
     960           1 :                                 return i.maybeVerifyKey(ikey, val)
     961           1 :                         }
     962             :                         // Next iteration will return if singleLevelIterator set
     963             :                         // exhaustedBounds = -1.
     964           1 :                 } else {
     965           1 :                         // result == loadBlockIrrelevant. Enforce the lower bound here
     966           1 :                         // since don't want to bother moving to the previous entry in the
     967           1 :                         // top level index if lower bound is already exceeded. Note that
     968           1 :                         // the previous entry starts with keys <= ikey.UserKey since even
     969           1 :                         // though this is the current block's separator, the same user key
     970           1 :                         // can span multiple index blocks.
     971           1 :                         if i.lower != nil && i.cmp(ikey.UserKey, i.lower) < 0 {
     972           0 :                                 i.exhaustedBounds = -1
     973           0 :                                 // Next iteration will return.
     974           0 :                         }
     975             :                 }
     976             :         }
     977             : }
     978             : 
     979             : // Close implements internalIterator.Close, as documented in the pebble
     980             : // package.
     981           1 : func (i *twoLevelIterator) Close() error {
     982           1 :         i.iterStats.close()
     983           1 :         var err error
     984           1 :         if i.closeHook != nil {
     985           1 :                 err = firstError(err, i.closeHook(i))
     986           1 :         }
     987           1 :         err = firstError(err, i.data.Close())
     988           1 :         err = firstError(err, i.index.Close())
     989           1 :         err = firstError(err, i.topLevelIndex.Close())
     990           1 :         if i.dataRH != nil {
     991           1 :                 err = firstError(err, i.dataRH.Close())
     992           1 :                 i.dataRH = nil
     993           1 :         }
     994           1 :         err = firstError(err, i.err)
     995           1 :         if i.bpfs != nil {
     996           1 :                 releaseBlockPropertiesFilterer(i.bpfs)
     997           1 :         }
     998           1 :         if i.vbReader != nil {
     999           1 :                 i.vbReader.close()
    1000           1 :         }
    1001           1 :         if i.vbRH != nil {
    1002           1 :                 err = firstError(err, i.vbRH.Close())
    1003           1 :                 i.vbRH = nil
    1004           1 :         }
    1005           1 :         *i = twoLevelIterator{
    1006           1 :                 singleLevelIterator: i.singleLevelIterator.resetForReuse(),
    1007           1 :                 topLevelIndex:       i.topLevelIndex.resetForReuse(),
    1008           1 :         }
    1009           1 :         twoLevelIterPool.Put(i)
    1010           1 :         return err
    1011             : }
    1012             : 
    1013             : // Note: twoLevelCompactionIterator and compactionIterator are very similar but
    1014             : // were separated due to performance.
    1015             : type twoLevelCompactionIterator struct {
    1016             :         *twoLevelIterator
    1017             :         bytesIterated *uint64
    1018             :         prevOffset    uint64
    1019             : }
    1020             : 
    1021             : // twoLevelCompactionIterator implements the base.InternalIterator interface.
    1022             : var _ base.InternalIterator = (*twoLevelCompactionIterator)(nil)
    1023             : 
    1024           1 : func (i *twoLevelCompactionIterator) Close() error {
    1025           1 :         return i.twoLevelIterator.Close()
    1026           1 : }
    1027             : 
    1028             : func (i *twoLevelCompactionIterator) SeekGE(
    1029             :         key []byte, flags base.SeekGEFlags,
    1030           0 : ) (*InternalKey, base.LazyValue) {
    1031           0 :         panic("pebble: SeekGE unimplemented")
    1032             : }
    1033             : 
    1034             : func (i *twoLevelCompactionIterator) SeekPrefixGE(
    1035             :         prefix, key []byte, flags base.SeekGEFlags,
    1036           0 : ) (*base.InternalKey, base.LazyValue) {
    1037           0 :         panic("pebble: SeekPrefixGE unimplemented")
    1038             : }
    1039             : 
    1040             : func (i *twoLevelCompactionIterator) SeekLT(
    1041             :         key []byte, flags base.SeekLTFlags,
    1042           0 : ) (*InternalKey, base.LazyValue) {
    1043           0 :         panic("pebble: SeekLT unimplemented")
    1044             : }
    1045             : 
    1046           1 : func (i *twoLevelCompactionIterator) First() (*InternalKey, base.LazyValue) {
    1047           1 :         i.err = nil // clear cached iteration error
    1048           1 :         return i.skipForward(i.twoLevelIterator.First())
    1049           1 : }
    1050             : 
    1051           0 : func (i *twoLevelCompactionIterator) Last() (*InternalKey, base.LazyValue) {
    1052           0 :         panic("pebble: Last unimplemented")
    1053             : }
    1054             : 
    1055             : // Note: twoLevelCompactionIterator.Next mirrors the implementation of
    1056             : // twoLevelIterator.Next due to performance. Keep the two in sync.
    1057           1 : func (i *twoLevelCompactionIterator) Next() (*InternalKey, base.LazyValue) {
    1058           1 :         if i.err != nil {
    1059           0 :                 return nil, base.LazyValue{}
    1060           0 :         }
    1061           1 :         return i.skipForward(i.singleLevelIterator.Next())
    1062             : }
    1063             : 
    1064           0 : func (i *twoLevelCompactionIterator) NextPrefix(succKey []byte) (*InternalKey, base.LazyValue) {
    1065           0 :         panic("pebble: NextPrefix unimplemented")
    1066             : }
    1067             : 
    1068           0 : func (i *twoLevelCompactionIterator) Prev() (*InternalKey, base.LazyValue) {
    1069           0 :         panic("pebble: Prev unimplemented")
    1070             : }
    1071             : 
    1072           0 : func (i *twoLevelCompactionIterator) String() string {
    1073           0 :         if i.vState != nil {
    1074           0 :                 return i.vState.fileNum.String()
    1075           0 :         }
    1076           0 :         return i.reader.fileNum.String()
    1077             : }
    1078             : 
    1079             : func (i *twoLevelCompactionIterator) skipForward(
    1080             :         key *InternalKey, val base.LazyValue,
    1081           1 : ) (*InternalKey, base.LazyValue) {
    1082           1 :         if key == nil {
    1083           1 :                 for {
    1084           1 :                         if key, _ := i.topLevelIndex.Next(); key == nil {
    1085           1 :                                 break
    1086             :                         }
    1087           1 :                         result := i.loadIndex(+1)
    1088           1 :                         if result != loadBlockOK {
    1089           0 :                                 if i.err != nil {
    1090           0 :                                         break
    1091             :                                 }
    1092           0 :                                 switch result {
    1093           0 :                                 case loadBlockFailed:
    1094           0 :                                         // We checked that i.index was at a valid entry, so
    1095           0 :                                         // loadBlockFailed could not have happened due to to i.index
    1096           0 :                                         // being exhausted, and must be due to an error.
    1097           0 :                                         panic("loadBlock should not have failed with no error")
    1098           0 :                                 case loadBlockIrrelevant:
    1099           0 :                                         panic("compactionIter should not be using block intervals for skipping")
    1100           0 :                                 default:
    1101           0 :                                         panic(fmt.Sprintf("unexpected case %d", result))
    1102             :                                 }
    1103             :                         }
    1104             :                         // result == loadBlockOK
    1105           1 :                         if key, val = i.singleLevelIterator.First(); key != nil {
    1106           1 :                                 break
    1107             :                         }
    1108             :                 }
    1109             :         }
    1110             : 
    1111           1 :         curOffset := i.recordOffset()
    1112           1 :         *i.bytesIterated += uint64(curOffset - i.prevOffset)
    1113           1 :         i.prevOffset = curOffset
    1114           1 : 
    1115           1 :         if i.vState != nil && key != nil {
    1116           1 :                 cmp := i.cmp(key.UserKey, i.vState.upper.UserKey)
    1117           1 :                 if cmp > 0 || (i.vState.upper.IsExclusiveSentinel() && cmp == 0) {
    1118           0 :                         return nil, base.LazyValue{}
    1119           0 :                 }
    1120             :         }
    1121             : 
    1122           1 :         return key, val
    1123             : }

Generated by: LCOV version 1.14