LCOV - code coverage report
Current view: top level - pebble/sstable/sstable - reader_iter.go (source / functions) Coverage Total Hit
Test: 2025-03-10 08:17Z de0c5a11 - meta test only.lcov Lines: 77.4 % 53 41
Test Date: 2025-03-10 08:18:31 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              :         "fmt"
       9              :         "os"
      10              :         "sync"
      11              : 
      12              :         "github.com/cockroachdb/pebble/internal/base"
      13              :         "github.com/cockroachdb/pebble/internal/invariants"
      14              :         "github.com/cockroachdb/pebble/sstable/block"
      15              :         "github.com/cockroachdb/pebble/sstable/colblk"
      16              :         "github.com/cockroachdb/pebble/sstable/rowblk"
      17              : )
      18              : 
      19              : // dataBlockIterator extends the block.IndexBlockIterator interface with a
      20              : // constraint that the implementing type be a pointer to a type I.
      21              : //
      22              : // DataBlockIterator requires that the type be a pointer to its type parameter,
      23              : // D, to allow sstable iterators embed the block iterator within its struct. See
      24              : // this example from the Go generics proposal:
      25              : // https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#pointer-method-example
      26              : type dataBlockIterator[D any] interface {
      27              :         block.DataBlockIterator
      28              : 
      29              :         *D // non-interface type constraint element
      30              : }
      31              : 
      32              : // indexBlockIterator extends the block.IndexBlockIterator interface with a
      33              : // constraint that the implementing type be a pointer to a type I.
      34              : //
      35              : // indexBlockIterator requires that the type be a pointer to its type parameter,
      36              : // I, to allow sstable iterators embed the block iterator within its struct. See
      37              : // this example from the Go generics proposal:
      38              : // https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#pointer-method-example
      39              : type indexBlockIterator[I any] interface {
      40              :         block.IndexBlockIterator
      41              : 
      42              :         *I // non-interface type constraint element
      43              : }
      44              : 
      45              : // Iterator iterates over an entire table of data.
      46              : type Iterator interface {
      47              :         base.InternalIterator
      48              : 
      49              :         // NextPrefix implements (base.InternalIterator).NextPrefix.
      50              :         NextPrefix(succKey []byte) *base.InternalKV
      51              : 
      52              :         // SetCloseHook sets a function that will be called when the iterator is
      53              :         // closed. This is used by the file cache to release the reference count on
      54              :         // the open sstable.Reader when the iterator is closed.
      55              :         SetCloseHook(func())
      56              : }
      57              : 
      58              : // Iterator positioning optimizations and singleLevelIterator and
      59              : // twoLevelIterator:
      60              : //
      61              : // An iterator is absolute positioned using one of the Seek or First or Last
      62              : // calls. After absolute positioning, there can be relative positioning done
      63              : // by stepping using Prev or Next.
      64              : //
      65              : // We implement optimizations below where an absolute positioning call can in
      66              : // some cases use the current position to do less work. To understand these,
      67              : // we first define some terms. An iterator is bounds-exhausted if the bounds
      68              : // (upper of lower) have been reached. An iterator is data-exhausted if it has
      69              : // the reached the end of the data (forward or reverse) in the sstable. A
      70              : // singleLevelIterator only knows a local-data-exhausted property since when
      71              : // it is used as part of a twoLevelIterator, the twoLevelIterator can step to
      72              : // the next lower-level index block.
      73              : //
      74              : // The bounds-exhausted property is tracked by
      75              : // singleLevelIterator.exhaustedBounds being +1 (upper bound reached) or -1
      76              : // (lower bound reached). The same field is reused by twoLevelIterator. Either
      77              : // may notice the exhaustion of the bound and set it. Note that if
      78              : // singleLevelIterator sets this property, it is not a local property (since
      79              : // the bound has been reached regardless of whether this is in the context of
      80              : // the twoLevelIterator or not).
      81              : //
      82              : // The data-exhausted property is tracked in a more subtle manner. We define
      83              : // two predicates:
      84              : // - partial-local-data-exhausted (PLDE):
      85              : //   i.data.IsDataInvalidated() || !i.data.Valid()
      86              : // - partial-global-data-exhausted (PGDE):
      87              : //   i.index.IsDataInvalidated() || !i.index.Valid() || i.data.IsDataInvalidated() ||
      88              : //   !i.data.Valid()
      89              : //
      90              : // PLDE is defined for a singleLevelIterator. PGDE is defined for a
      91              : // twoLevelIterator. Oddly, in our code below the singleLevelIterator does not
      92              : // know when it is part of a twoLevelIterator so it does not know when its
      93              : // property is local or global.
      94              : //
      95              : // Now to define data-exhausted:
      96              : // - Prerequisite: we must know that the iterator has been positioned and
      97              : //   i.err is nil.
      98              : // - bounds-exhausted must not be true:
      99              : //   If bounds-exhausted is true, we have incomplete knowledge of
     100              : //   data-exhausted since PLDE or PGDE could be true because we could have
     101              : //   chosen not to load index block or data block and figured out that the
     102              : //   bound is exhausted (due to block property filters filtering out index and
     103              : //   data blocks and going past the bound on the top level index block). Note
     104              : //   that if we tried to separate out the BPF case from others we could
     105              : //   develop more knowledge here.
     106              : // - PGDE is true for twoLevelIterator. PLDE is true if it is a standalone
     107              : //   singleLevelIterator. !PLDE or !PGDE of course imply that data-exhausted
     108              : //   is not true.
     109              : //
     110              : // An implication of the above is that if we are going to somehow utilize
     111              : // knowledge of data-exhausted in an optimization, we must not forget the
     112              : // existing value of bounds-exhausted since by forgetting the latter we can
     113              : // erroneously think that data-exhausted is true. Bug #2036 was due to this
     114              : // forgetting.
     115              : //
     116              : // Now to the two categories of optimizations we currently have:
     117              : // - Monotonic bounds optimization that reuse prior iterator position when
     118              : //   doing seek: These only work with !data-exhausted. We could choose to make
     119              : //   these work with data-exhausted but have not bothered because in the
     120              : //   context of a DB if data-exhausted were true, the DB would move to the
     121              : //   next file in the level. Note that this behavior of moving to the next
     122              : //   file is not necessarily true for L0 files, so there could be some benefit
     123              : //   in the future in this optimization. See the WARNING-data-exhausted
     124              : //   comments if trying to optimize this in the future.
     125              : // - TrySeekUsingNext optimizations: these work regardless of exhaustion
     126              : //   state.
     127              : //
     128              : // Implementation detail: In the code PLDE only checks that
     129              : // i.data.IsDataInvalidated(). This narrower check is safe, since this is a
     130              : // subset of the set expressed by the OR expression. Also, it is not a
     131              : // de-optimization since whenever we exhaust the iterator we explicitly call
     132              : // i.data.Invalidate(). PGDE checks i.index.IsDataInvalidated() &&
     133              : // i.data.IsDataInvalidated(). Again, this narrower check is safe, and not a
     134              : // de-optimization since whenever we exhaust the iterator we explicitly call
     135              : // i.index.Invalidate() and i.data.Invalidate(). The && is questionable -- for
     136              : // now this is a bit of defensive code. We should seriously consider removing
     137              : // it, since defensive code suggests we are not confident about our invariants
     138              : // (and if we are not confident, we need more invariant assertions, not
     139              : // defensive code).
     140              : //
     141              : // TODO(sumeer): remove the aforementioned defensive code.
     142              : 
     143              : type (
     144              :         singleLevelIteratorRowBlocks    = singleLevelIterator[rowblk.IndexIter, *rowblk.IndexIter, rowblk.Iter, *rowblk.Iter]
     145              :         twoLevelIteratorRowBlocks       = twoLevelIterator[rowblk.IndexIter, *rowblk.IndexIter, rowblk.Iter, *rowblk.Iter]
     146              :         singleLevelIteratorColumnBlocks = singleLevelIterator[colblk.IndexIter, *colblk.IndexIter, colblk.DataBlockIter, *colblk.DataBlockIter]
     147              :         twoLevelIteratorColumnBlocks    = twoLevelIterator[colblk.IndexIter, *colblk.IndexIter, colblk.DataBlockIter, *colblk.DataBlockIter]
     148              : )
     149              : 
     150              : var (
     151              :         singleLevelIterRowBlockPool    sync.Pool // *singleLevelIteratorRowBlocks
     152              :         twoLevelIterRowBlockPool       sync.Pool // *twoLevelIteratorRowBlocks
     153              :         singleLevelIterColumnBlockPool sync.Pool // *singleLevelIteratorColumnBlocks
     154              :         twoLevelIterColumnBlockPool    sync.Pool // *singleLevelIteratorColumnBlocks
     155              : )
     156              : 
     157            1 : func init() {
     158            1 :         singleLevelIterRowBlockPool = sync.Pool{
     159            1 :                 New: func() interface{} {
     160            1 :                         i := &singleLevelIteratorRowBlocks{pool: &singleLevelIterRowBlockPool}
     161            1 :                         if invariants.UseFinalizers {
     162            1 :                                 invariants.SetFinalizer(i, checkSingleLevelIterator[rowblk.IndexIter, *rowblk.IndexIter, rowblk.Iter, *rowblk.Iter])
     163            1 :                         }
     164            1 :                         return i
     165              :                 },
     166              :         }
     167            1 :         twoLevelIterRowBlockPool = sync.Pool{
     168            1 :                 New: func() interface{} {
     169            1 :                         i := &twoLevelIteratorRowBlocks{pool: &twoLevelIterRowBlockPool}
     170            1 :                         if invariants.UseFinalizers {
     171            1 :                                 invariants.SetFinalizer(i, checkTwoLevelIterator[rowblk.IndexIter, *rowblk.IndexIter, rowblk.Iter, *rowblk.Iter])
     172            1 :                         }
     173            1 :                         return i
     174              :                 },
     175              :         }
     176            1 :         singleLevelIterColumnBlockPool = sync.Pool{
     177            1 :                 New: func() interface{} {
     178            1 :                         i := &singleLevelIteratorColumnBlocks{
     179            1 :                                 pool: &singleLevelIterColumnBlockPool,
     180            1 :                         }
     181            1 :                         if invariants.UseFinalizers {
     182            1 :                                 invariants.SetFinalizer(i, checkSingleLevelIterator[colblk.IndexIter, *colblk.IndexIter, colblk.DataBlockIter, *colblk.DataBlockIter])
     183            1 :                         }
     184            1 :                         return i
     185              :                 },
     186              :         }
     187            1 :         twoLevelIterColumnBlockPool = sync.Pool{
     188            1 :                 New: func() interface{} {
     189            1 :                         i := &twoLevelIteratorColumnBlocks{
     190            1 :                                 pool: &twoLevelIterColumnBlockPool,
     191            1 :                         }
     192            1 :                         if invariants.UseFinalizers {
     193            1 :                                 invariants.SetFinalizer(i, checkTwoLevelIterator[colblk.IndexIter, *colblk.IndexIter, colblk.DataBlockIter, *colblk.DataBlockIter])
     194            1 :                         }
     195            1 :                         return i
     196              :                 },
     197              :         }
     198              : }
     199              : 
     200              : func checkSingleLevelIterator[I any, PI indexBlockIterator[I], D any, PD dataBlockIterator[D]](
     201              :         obj interface{},
     202            1 : ) {
     203            1 :         i := obj.(*singleLevelIterator[I, PI, D, PD])
     204            1 :         if h := PD(&i.data).Handle(); h.Valid() {
     205            0 :                 fmt.Fprintf(os.Stderr, "singleLevelIterator.data.handle is not nil: %#v\n", h)
     206            0 :                 os.Exit(1)
     207            0 :         }
     208            1 :         if h := PI(&i.index).Handle(); h.Valid() {
     209            0 :                 fmt.Fprintf(os.Stderr, "singleLevelIterator.index.handle is not nil: %#v\n", h)
     210            0 :                 os.Exit(1)
     211            0 :         }
     212              : }
     213              : 
     214              : func checkTwoLevelIterator[I any, PI indexBlockIterator[I], D any, PD dataBlockIterator[D]](
     215              :         obj interface{},
     216            1 : ) {
     217            1 :         i := obj.(*twoLevelIterator[I, PI, D, PD])
     218            1 :         if h := PD(&i.secondLevel.data).Handle(); h.Valid() {
     219            0 :                 fmt.Fprintf(os.Stderr, "singleLevelIterator.data.handle is not nil: %#v\n", h)
     220            0 :                 os.Exit(1)
     221            0 :         }
     222            1 :         if h := PI(&i.secondLevel.index).Handle(); h.Valid() {
     223            0 :                 fmt.Fprintf(os.Stderr, "singleLevelIterator.index.handle is not nil: %#v\n", h)
     224            0 :                 os.Exit(1)
     225            0 :         }
     226              : }
        

Generated by: LCOV version 2.0-1