github.com/cockroachdb/pebble@v1.1.1-0.20240513155919-3622ade60459/internal/keyspan/bounded.go (about)

     1  // Copyright 2022 The LevelDB-Go and Pebble Authors. All rights reserved. Use
     2  // of this source code is governed by a BSD-style license that can be found in
     3  // the LICENSE file.
     4  
     5  package keyspan
     6  
     7  import "github.com/cockroachdb/pebble/internal/base"
     8  
     9  // TODO(jackson): Consider removing this type and adding bounds enforcement
    10  // directly to the MergingIter. This type is probably too lightweight to warrant
    11  // its own type, but for now we implement it separately for expediency.
    12  
    13  // boundedIterPos records the position of the BoundedIter relative to the
    14  // underlying iterator's position. It's used to avoid Next/Prev-ing the iterator
    15  // if there can't possibly be another span within bounds, because the current
    16  // span overlaps the bound.
    17  //
    18  // Imagine bounds [a,c) and an iterator that seeks to a span [b,d). The span
    19  // [b,d) overlaps some portion of the iterator bounds, so the iterator must
    20  // return it. If the iterator is subsequently Nexted, Next can tell that the
    21  // iterator is exhausted without advancing the underlying iterator because the
    22  // current span's end bound of d is ≥ the upper bound of c. In this case, the
    23  // bounded iterator returns nil and records i.pos as posAtUpperLimit to remember
    24  // that the underlying iterator position does not match the current BoundedIter
    25  // position.
    26  type boundedIterPos int8
    27  
    28  const (
    29  	posAtLowerLimit boundedIterPos = -1
    30  	posAtIterSpan   boundedIterPos = 0
    31  	posAtUpperLimit boundedIterPos = +1
    32  )
    33  
    34  // BoundedIter implements FragmentIterator and enforces bounds.
    35  //
    36  // Like the point InternalIterator interface, the bounded iterator's forward
    37  // positioning routines (SeekGE, First, and Next) only check the upper bound.
    38  // The reverse positioning routines (SeekLT, Last, and Prev) only check the
    39  // lower bound. It is up to the caller to ensure that the forward positioning
    40  // routines respect the lower bound and the reverse positioning routines respect
    41  // the upper bound (i.e. calling SeekGE instead of First if there is a lower
    42  // bound, and SeekLT instead of Last if there is an upper bound).
    43  //
    44  // When the hasPrefix parameter indicates that the iterator is in prefix
    45  // iteration mode, BoundedIter elides any spans that do not overlap with the
    46  // prefix's keyspace. In prefix iteration mode, reverse iteration is disallowed,
    47  // except for an initial SeekLT with a seek key greater than or equal to the
    48  // prefix. In prefix iteration mode, the first seek must position the iterator
    49  // at or immediately before the first fragment covering a key greater than or
    50  // equal to the prefix.
    51  type BoundedIter struct {
    52  	iter      FragmentIterator
    53  	iterSpan  *Span
    54  	cmp       base.Compare
    55  	split     base.Split
    56  	lower     []byte
    57  	upper     []byte
    58  	hasPrefix *bool
    59  	prefix    *[]byte
    60  	pos       boundedIterPos
    61  }
    62  
    63  // Init initializes the bounded iterator.
    64  //
    65  // In addition to the iterator bounds, Init takes pointers to a boolean
    66  // indicating whether the iterator is in prefix iteration mode and the prefix
    67  // key if it is. This is used to exclude spans that are outside the iteration
    68  // prefix.
    69  //
    70  // hasPrefix and prefix are allowed to be nil, however if hasPrefix != nil,
    71  // prefix must also not be nil.
    72  func (i *BoundedIter) Init(
    73  	cmp base.Compare,
    74  	split base.Split,
    75  	iter FragmentIterator,
    76  	lower, upper []byte,
    77  	hasPrefix *bool,
    78  	prefix *[]byte,
    79  ) {
    80  	*i = BoundedIter{
    81  		iter:      iter,
    82  		cmp:       cmp,
    83  		split:     split,
    84  		lower:     lower,
    85  		upper:     upper,
    86  		hasPrefix: hasPrefix,
    87  		prefix:    prefix,
    88  	}
    89  }
    90  
    91  var _ FragmentIterator = (*BoundedIter)(nil)
    92  
    93  // Seek calls.
    94  //
    95  // Seek calls check iterator bounds in the direction of the seek. Additionally,
    96  // if the iterator is in prefix iteration mode, seek calls check both start and
    97  // end bounds against the prefix's bounds. We check both bounds for defense in
    98  // depth. This optimization has been a source of various bugs due to various
    99  // other prefix iteration optimizations that can result in seek keys that don't
   100  // respect the prefix bounds.
   101  
   102  // SeekGE implements FragmentIterator.
   103  func (i *BoundedIter) SeekGE(key []byte) *Span {
   104  	s := i.iter.SeekGE(key)
   105  	s = i.checkPrefixSpanStart(s)
   106  	s = i.checkPrefixSpanEnd(s)
   107  	return i.checkForwardBound(s)
   108  }
   109  
   110  // SeekLT implements FragmentIterator.
   111  func (i *BoundedIter) SeekLT(key []byte) *Span {
   112  	s := i.iter.SeekLT(key)
   113  	s = i.checkPrefixSpanStart(s)
   114  	s = i.checkPrefixSpanEnd(s)
   115  	return i.checkBackwardBound(s)
   116  }
   117  
   118  // First implements FragmentIterator.
   119  func (i *BoundedIter) First() *Span {
   120  	s := i.iter.First()
   121  	s = i.checkPrefixSpanStart(s)
   122  	return i.checkForwardBound(s)
   123  }
   124  
   125  // Last implements FragmentIterator.
   126  func (i *BoundedIter) Last() *Span {
   127  	s := i.iter.Last()
   128  	s = i.checkPrefixSpanEnd(s)
   129  	return i.checkBackwardBound(s)
   130  }
   131  
   132  // Next implements FragmentIterator.
   133  func (i *BoundedIter) Next() *Span {
   134  	switch i.pos {
   135  	case posAtLowerLimit:
   136  		// The BoundedIter had previously returned nil, because it knew from
   137  		// i.iterSpan's bounds that there was no previous span. To Next, we only
   138  		// need to return the current iter span and reset i.pos to reflect that
   139  		// we're no longer positioned at the limit.
   140  		i.pos = posAtIterSpan
   141  		return i.iterSpan
   142  	case posAtIterSpan:
   143  		// If the span at the underlying iterator position extends to or beyond the
   144  		// upper bound, we can avoid advancing because the next span is necessarily
   145  		// out of bounds.
   146  		if i.iterSpan != nil && i.upper != nil && i.cmp(i.iterSpan.End, i.upper) >= 0 {
   147  			i.pos = posAtUpperLimit
   148  			return nil
   149  		}
   150  		// Similarly, if the span extends to the next prefix and we're in prefix
   151  		// iteration mode, we can avoid advancing.
   152  		if i.iterSpan != nil && i.hasPrefix != nil && *i.hasPrefix {
   153  			ei := i.split(i.iterSpan.End)
   154  			if i.cmp(i.iterSpan.End[:ei], *i.prefix) > 0 {
   155  				i.pos = posAtUpperLimit
   156  				return nil
   157  			}
   158  		}
   159  		return i.checkForwardBound(i.checkPrefixSpanStart(i.iter.Next()))
   160  	case posAtUpperLimit:
   161  		// Already exhausted.
   162  		return nil
   163  	default:
   164  		panic("unreachable")
   165  	}
   166  }
   167  
   168  // Prev implements FragmentIterator.
   169  func (i *BoundedIter) Prev() *Span {
   170  	switch i.pos {
   171  	case posAtLowerLimit:
   172  		// Already exhausted.
   173  		return nil
   174  	case posAtIterSpan:
   175  		// If the span at the underlying iterator position extends to or beyond
   176  		// the lower bound, we can avoid advancing because the previous span is
   177  		// necessarily out of bounds.
   178  		if i.iterSpan != nil && i.lower != nil && i.cmp(i.iterSpan.Start, i.lower) <= 0 {
   179  			i.pos = posAtLowerLimit
   180  			return nil
   181  		}
   182  		// Similarly, if the span extends to or beyond the current prefix and
   183  		// we're in prefix iteration mode, we can avoid advancing.
   184  		if i.iterSpan != nil && i.hasPrefix != nil && *i.hasPrefix {
   185  			si := i.split(i.iterSpan.Start)
   186  			if i.cmp(i.iterSpan.Start[:si], *i.prefix) < 0 {
   187  				i.pos = posAtLowerLimit
   188  				return nil
   189  			}
   190  		}
   191  		return i.checkBackwardBound(i.checkPrefixSpanEnd(i.iter.Prev()))
   192  	case posAtUpperLimit:
   193  		// The BoundedIter had previously returned nil, because it knew from
   194  		// i.iterSpan's bounds that there was no next span. To Prev, we only
   195  		// need to return the current iter span and reset i.pos to reflect that
   196  		// we're no longer positioned at the limit.
   197  		i.pos = posAtIterSpan
   198  		return i.iterSpan
   199  	default:
   200  		panic("unreachable")
   201  	}
   202  }
   203  
   204  // Error implements FragmentIterator.
   205  func (i *BoundedIter) Error() error {
   206  	return i.iter.Error()
   207  }
   208  
   209  // Close implements FragmentIterator.
   210  func (i *BoundedIter) Close() error {
   211  	return i.iter.Close()
   212  }
   213  
   214  // SetBounds modifies the FragmentIterator's bounds.
   215  func (i *BoundedIter) SetBounds(lower, upper []byte) {
   216  	i.lower, i.upper = lower, upper
   217  }
   218  
   219  func (i *BoundedIter) checkPrefixSpanStart(span *Span) *Span {
   220  	// Compare to the prefix's bounds, if in prefix iteration mode.
   221  	if span != nil && i.hasPrefix != nil && *i.hasPrefix {
   222  		si := i.split(span.Start)
   223  		if i.cmp(span.Start[:si], *i.prefix) > 0 {
   224  			// This span starts at a prefix that sorts after our current prefix.
   225  			span = nil
   226  		}
   227  	}
   228  	return span
   229  }
   230  
   231  // checkForwardBound enforces the upper bound, returning nil if the provided
   232  // span is wholly outside the upper bound. It also updates i.pos and i.iterSpan
   233  // to reflect the new iterator position.
   234  func (i *BoundedIter) checkForwardBound(span *Span) *Span {
   235  	// Compare to the upper bound.
   236  	if span != nil && i.upper != nil && i.cmp(span.Start, i.upper) >= 0 {
   237  		span = nil
   238  	}
   239  	i.iterSpan = span
   240  	if i.pos != posAtIterSpan {
   241  		i.pos = posAtIterSpan
   242  	}
   243  	return span
   244  }
   245  
   246  func (i *BoundedIter) checkPrefixSpanEnd(span *Span) *Span {
   247  	// Compare to the prefix's bounds, if in prefix iteration mode.
   248  	if span != nil && i.hasPrefix != nil && *i.hasPrefix && i.cmp(span.End, *i.prefix) <= 0 {
   249  		// This span ends before the current prefix.
   250  		span = nil
   251  	}
   252  	return span
   253  }
   254  
   255  // checkBackward enforces the lower bound, returning nil if the provided span is
   256  // wholly outside the lower bound.  It also updates i.pos and i.iterSpan to
   257  // reflect the new iterator position.
   258  func (i *BoundedIter) checkBackwardBound(span *Span) *Span {
   259  	// Compare to the lower bound.
   260  	if span != nil && i.lower != nil && i.cmp(span.End, i.lower) <= 0 {
   261  		span = nil
   262  	}
   263  	i.iterSpan = span
   264  	if i.pos != posAtIterSpan {
   265  		i.pos = posAtIterSpan
   266  	}
   267  	return span
   268  }