LCOV - code coverage report
Current view: top level - pebble/sstable - reader_virtual.go (source / functions) Hit Total Coverage
Test: 2023-12-14 08:16Z 288bf0fb - tests + meta.lcov Lines: 92 97 94.8 %
Date: 2023-12-14 08:17:14 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             :         "context"
       9             : 
      10             :         "github.com/cockroachdb/pebble/internal/base"
      11             :         "github.com/cockroachdb/pebble/internal/keyspan"
      12             :         "github.com/cockroachdb/pebble/internal/manifest"
      13             : )
      14             : 
      15             : // VirtualReader wraps Reader. Its purpose is to restrict functionality of the
      16             : // Reader which should be inaccessible to virtual sstables, and enforce bounds
      17             : // invariants associated with virtual sstables. All reads on virtual sstables
      18             : // should go through a VirtualReader.
      19             : //
      20             : // INVARIANT: Any iterators created through a virtual reader will guarantee that
      21             : // they don't expose keys outside the virtual sstable bounds.
      22             : type VirtualReader struct {
      23             :         vState     virtualState
      24             :         reader     *Reader
      25             :         Properties CommonProperties
      26             : }
      27             : 
      28             : // Lightweight virtual sstable state which can be passed to sstable iterators.
      29             : type virtualState struct {
      30             :         lower     InternalKey
      31             :         upper     InternalKey
      32             :         fileNum   base.FileNum
      33             :         Compare   Compare
      34             :         isForeign bool
      35             : }
      36             : 
      37           2 : func ceilDiv(a, b uint64) uint64 {
      38           2 :         return (a + b - 1) / b
      39           2 : }
      40             : 
      41             : // MakeVirtualReader is used to contruct a reader which can read from virtual
      42             : // sstables.
      43             : func MakeVirtualReader(
      44             :         reader *Reader, meta manifest.VirtualFileMeta, isForeign bool,
      45           2 : ) VirtualReader {
      46           2 :         if reader.fileNum != meta.FileBacking.DiskFileNum {
      47           0 :                 panic("pebble: invalid call to MakeVirtualReader")
      48             :         }
      49             : 
      50           2 :         vState := virtualState{
      51           2 :                 lower:     meta.Smallest,
      52           2 :                 upper:     meta.Largest,
      53           2 :                 fileNum:   meta.FileNum,
      54           2 :                 Compare:   reader.Compare,
      55           2 :                 isForeign: isForeign,
      56           2 :         }
      57           2 :         v := VirtualReader{
      58           2 :                 vState: vState,
      59           2 :                 reader: reader,
      60           2 :         }
      61           2 : 
      62           2 :         v.Properties.RawKeySize = ceilDiv(reader.Properties.RawKeySize*meta.Size, meta.FileBacking.Size)
      63           2 :         v.Properties.RawValueSize = ceilDiv(reader.Properties.RawValueSize*meta.Size, meta.FileBacking.Size)
      64           2 :         v.Properties.NumEntries = ceilDiv(reader.Properties.NumEntries*meta.Size, meta.FileBacking.Size)
      65           2 :         v.Properties.NumDeletions = ceilDiv(reader.Properties.NumDeletions*meta.Size, meta.FileBacking.Size)
      66           2 :         v.Properties.NumRangeDeletions = ceilDiv(reader.Properties.NumRangeDeletions*meta.Size, meta.FileBacking.Size)
      67           2 :         v.Properties.NumRangeKeyDels = ceilDiv(reader.Properties.NumRangeKeyDels*meta.Size, meta.FileBacking.Size)
      68           2 : 
      69           2 :         // Note that we rely on NumRangeKeySets for correctness. If the sstable may
      70           2 :         // contain range keys, then NumRangeKeySets must be > 0. ceilDiv works because
      71           2 :         // meta.Size will not be 0 for virtual sstables.
      72           2 :         v.Properties.NumRangeKeySets = ceilDiv(reader.Properties.NumRangeKeySets*meta.Size, meta.FileBacking.Size)
      73           2 :         v.Properties.ValueBlocksSize = ceilDiv(reader.Properties.ValueBlocksSize*meta.Size, meta.FileBacking.Size)
      74           2 :         v.Properties.NumSizedDeletions = ceilDiv(reader.Properties.NumSizedDeletions*meta.Size, meta.FileBacking.Size)
      75           2 :         v.Properties.RawPointTombstoneKeySize = ceilDiv(reader.Properties.RawPointTombstoneKeySize*meta.Size, meta.FileBacking.Size)
      76           2 :         v.Properties.RawPointTombstoneValueSize = ceilDiv(reader.Properties.RawPointTombstoneValueSize*meta.Size, meta.FileBacking.Size)
      77           2 :         return v
      78             : }
      79             : 
      80             : // NewCompactionIter is the compaction iterator function for virtual readers.
      81             : func (v *VirtualReader) NewCompactionIter(
      82             :         bytesIterated *uint64,
      83             :         categoryAndQoS CategoryAndQoS,
      84             :         statsCollector *CategoryStatsCollector,
      85             :         rp ReaderProvider,
      86             :         bufferPool *BufferPool,
      87           2 : ) (Iterator, error) {
      88           2 :         return v.reader.newCompactionIter(
      89           2 :                 bytesIterated, categoryAndQoS, statsCollector, rp, &v.vState, bufferPool)
      90           2 : }
      91             : 
      92             : // NewIterWithBlockPropertyFiltersAndContextEtc wraps
      93             : // Reader.NewIterWithBlockPropertyFiltersAndContext. We assume that the passed
      94             : // in [lower, upper) bounds will have at least some overlap with the virtual
      95             : // sstable bounds. No overlap is not currently supported in the iterator.
      96             : func (v *VirtualReader) NewIterWithBlockPropertyFiltersAndContextEtc(
      97             :         ctx context.Context,
      98             :         lower, upper []byte,
      99             :         filterer *BlockPropertiesFilterer,
     100             :         hideObsoletePoints, useFilterBlock bool,
     101             :         stats *base.InternalIteratorStats,
     102             :         categoryAndQoS CategoryAndQoS,
     103             :         statsCollector *CategoryStatsCollector,
     104             :         rp ReaderProvider,
     105           2 : ) (Iterator, error) {
     106           2 :         return v.reader.newIterWithBlockPropertyFiltersAndContext(
     107           2 :                 ctx, lower, upper, filterer, hideObsoletePoints, useFilterBlock, stats,
     108           2 :                 categoryAndQoS, statsCollector, rp, &v.vState)
     109           2 : }
     110             : 
     111             : // ValidateBlockChecksumsOnBacking will call ValidateBlockChecksumsOnBacking on the underlying reader.
     112             : // Note that block checksum validation is NOT restricted to virtual sstable bounds.
     113           1 : func (v *VirtualReader) ValidateBlockChecksumsOnBacking() error {
     114           1 :         return v.reader.ValidateBlockChecksums()
     115           1 : }
     116             : 
     117             : // NewRawRangeDelIter wraps Reader.NewRawRangeDelIter.
     118           2 : func (v *VirtualReader) NewRawRangeDelIter() (keyspan.FragmentIterator, error) {
     119           2 :         iter, err := v.reader.NewRawRangeDelIter()
     120           2 :         if err != nil {
     121           0 :                 return nil, err
     122           0 :         }
     123           2 :         if iter == nil {
     124           2 :                 return nil, nil
     125           2 :         }
     126             : 
     127             :         // Truncation of spans isn't allowed at a user key that also contains points
     128             :         // in the same virtual sstable, as it would lead to covered points getting
     129             :         // uncovered. Set panicOnUpperTruncate to true if the file's upper bound
     130             :         // is not an exclusive sentinel.
     131             :         //
     132             :         // As an example, if an sstable contains a rangedel a-c and point keys at
     133             :         // a.SET.2 and b.SET.3, the file bounds [a#2,SET-b#RANGEDELSENTINEL] are
     134             :         // allowed (as they exclude b.SET.3), or [a#2,SET-c#RANGEDELSENTINEL] (as it
     135             :         // includes both point keys), but not [a#2,SET-b#3,SET] (as it would truncate
     136             :         // the rangedel at b and lead to the point being uncovered).
     137           2 :         return keyspan.Truncate(
     138           2 :                 v.reader.Compare, iter, v.vState.lower.UserKey, v.vState.upper.UserKey,
     139           2 :                 &v.vState.lower, &v.vState.upper, !v.vState.upper.IsExclusiveSentinel(), /* panicOnUpperTruncate */
     140           2 :         ), nil
     141             : }
     142             : 
     143             : // NewRawRangeKeyIter wraps Reader.NewRawRangeKeyIter.
     144           2 : func (v *VirtualReader) NewRawRangeKeyIter() (keyspan.FragmentIterator, error) {
     145           2 :         iter, err := v.reader.NewRawRangeKeyIter()
     146           2 :         if err != nil {
     147           0 :                 return nil, err
     148           0 :         }
     149           2 :         if iter == nil {
     150           2 :                 return nil, nil
     151           2 :         }
     152             : 
     153             :         // Truncation of spans isn't allowed at a user key that also contains points
     154             :         // in the same virtual sstable, as it would lead to covered points getting
     155             :         // uncovered. Set panicOnUpperTruncate to true if the file's upper bound
     156             :         // is not an exclusive sentinel.
     157             :         //
     158             :         // As an example, if an sstable contains a range key a-c and point keys at
     159             :         // a.SET.2 and b.SET.3, the file bounds [a#2,SET-b#RANGEKEYSENTINEL] are
     160             :         // allowed (as they exclude b.SET.3), or [a#2,SET-c#RANGEKEYSENTINEL] (as it
     161             :         // includes both point keys), but not [a#2,SET-b#3,SET] (as it would truncate
     162             :         // the range key at b and lead to the point being uncovered).
     163           2 :         return keyspan.Truncate(
     164           2 :                 v.reader.Compare, iter, v.vState.lower.UserKey, v.vState.upper.UserKey,
     165           2 :                 &v.vState.lower, &v.vState.upper, !v.vState.upper.IsExclusiveSentinel(), /* panicOnUpperTruncate */
     166           2 :         ), nil
     167             : }
     168             : 
     169             : // Constrain bounds will narrow the start, end bounds if they do not fit within
     170             : // the virtual sstable. The function will return if the new end key is
     171             : // inclusive.
     172             : func (v *virtualState) constrainBounds(
     173             :         start, end []byte, endInclusive bool,
     174           2 : ) (lastKeyInclusive bool, first []byte, last []byte) {
     175           2 :         first = start
     176           2 :         if start == nil || v.Compare(start, v.lower.UserKey) < 0 {
     177           2 :                 first = v.lower.UserKey
     178           2 :         }
     179             : 
     180             :         // Note that we assume that start, end has some overlap with the virtual
     181             :         // sstable bounds.
     182           2 :         last = v.upper.UserKey
     183           2 :         lastKeyInclusive = !v.upper.IsExclusiveSentinel()
     184           2 :         if end != nil {
     185           2 :                 cmp := v.Compare(end, v.upper.UserKey)
     186           2 :                 switch {
     187           2 :                 case cmp == 0:
     188           2 :                         lastKeyInclusive = !v.upper.IsExclusiveSentinel() && endInclusive
     189           2 :                         last = v.upper.UserKey
     190           2 :                 case cmp > 0:
     191           2 :                         lastKeyInclusive = !v.upper.IsExclusiveSentinel()
     192           2 :                         last = v.upper.UserKey
     193           2 :                 default:
     194           2 :                         lastKeyInclusive = endInclusive
     195           2 :                         last = end
     196             :                 }
     197             :         }
     198             :         // TODO(bananabrick): What if someone passes in bounds completely outside of
     199             :         // virtual sstable bounds?
     200           2 :         return lastKeyInclusive, first, last
     201             : }
     202             : 
     203             : // EstimateDiskUsage just calls VirtualReader.reader.EstimateDiskUsage after
     204             : // enforcing the virtual sstable bounds.
     205           2 : func (v *VirtualReader) EstimateDiskUsage(start, end []byte) (uint64, error) {
     206           2 :         _, f, l := v.vState.constrainBounds(start, end, true /* endInclusive */)
     207           2 :         return v.reader.EstimateDiskUsage(f, l)
     208           2 : }
     209             : 
     210             : // CommonProperties implements the CommonReader interface.
     211           2 : func (v *VirtualReader) CommonProperties() *CommonProperties {
     212           2 :         return &v.Properties
     213           2 : }

Generated by: LCOV version 1.14