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 }