github.com/zuoyebang/bitalostable@v1.0.1-0.20240229032404-e3b99a834294/internal/keyspan/level_iter.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 ( 8 "fmt" 9 10 "github.com/zuoyebang/bitalostable/internal/base" 11 "github.com/zuoyebang/bitalostable/internal/manifest" 12 ) 13 14 // Logger defines an interface for writing log messages. 15 type Logger interface { 16 Infof(format string, args ...interface{}) 17 Fatalf(format string, args ...interface{}) 18 } 19 20 // LevelIter provides a merged view of spans from sstables in a level. 21 // It takes advantage of level invariants to only have one sstable span block 22 // open at one time, opened using the newIter function passed in. 23 type LevelIter struct { 24 logger Logger 25 cmp base.Compare 26 // Denotes if this level iter should read point key spans (i.e. rangedels, 27 // or range keys. If key type is Point, no straddle spans are emitted between 28 // files, and point key bounds are used to find files instead of range key 29 // bounds. 30 // 31 // TODO(bilal): Straddle spans can safely be produced in rangedel mode once 32 // we can guarantee that we will never read sstables in a level that split 33 // user keys across them. This might be guaranteed in a future release, but 34 // as of CockroachDB 22.2 it is not guaranteed, so to be safe disable it when 35 // keyType == KeyTypePoint 36 keyType manifest.KeyType 37 // The LSM level this LevelIter is initialized for. Used in logging. 38 level manifest.Level 39 // The below fields are used to fill in gaps between adjacent files' range 40 // key spaces. This is an optimization to avoid unnecessarily loading files 41 // in cases where range keys are sparse and rare. dir is set by every 42 // positioning operation, straddleDir is set to dir whenever a straddling 43 // Span is synthesized and the last positioning operation returned a 44 // synthesized straddle span. 45 // 46 // Note that when a straddle span is initialized, iterFile is modified to 47 // point to the next file in the straddleDir direction. A change of direction 48 // on a straddle key therefore necessitates the value of iterFile to be 49 // reverted. 50 dir int 51 straddle Span 52 straddleDir int 53 // The iter for the current file. It is nil under any of the following conditions: 54 // - files.Current() == nil 55 // - err != nil 56 // - straddleDir != 0, in which case iterFile is not nil and points to the 57 // next file (in the straddleDir direction). 58 // - some other constraint, like the bounds in opts, caused the file at index to not 59 // be relevant to the iteration. 60 iter FragmentIterator 61 iterFile *manifest.FileMetadata 62 newIter TableNewSpanIter 63 files manifest.LevelIterator 64 err error 65 66 // The options that were passed in. 67 tableOpts SpanIterOptions 68 69 // TODO(bilal): Add InternalIteratorStats. 70 } 71 72 // LevelIter implements the keyspan.FragmentIterator interface. 73 var _ FragmentIterator = (*LevelIter)(nil) 74 75 // newLevelIter returns a LevelIter. 76 func newLevelIter( 77 opts SpanIterOptions, 78 cmp base.Compare, 79 newIter TableNewSpanIter, 80 files manifest.LevelIterator, 81 level manifest.Level, 82 logger Logger, 83 keyType manifest.KeyType, 84 ) *LevelIter { 85 l := &LevelIter{} 86 l.Init(opts, cmp, newIter, files, level, logger, keyType) 87 return l 88 } 89 90 // Init initializes a LevelIter. 91 func (l *LevelIter) Init( 92 opts SpanIterOptions, 93 cmp base.Compare, 94 newIter TableNewSpanIter, 95 files manifest.LevelIterator, 96 level manifest.Level, 97 logger Logger, 98 keyType manifest.KeyType, 99 ) { 100 l.err = nil 101 l.level = level 102 l.logger = logger 103 l.tableOpts.RangeKeyFilters = opts.RangeKeyFilters 104 l.cmp = cmp 105 l.iterFile = nil 106 l.newIter = newIter 107 switch keyType { 108 case manifest.KeyTypePoint, manifest.KeyTypeRange: 109 l.keyType = keyType 110 l.files = files.Filter(keyType) 111 default: 112 panic(fmt.Sprintf("unsupported key type: %v", keyType)) 113 } 114 } 115 116 func (l *LevelIter) findFileGE(key []byte) *manifest.FileMetadata { 117 // Find the earliest file whose largest key is >= key. 118 // 119 // If the earliest file has its largest key == key and that largest key is a 120 // range deletion sentinel, we know that we manufactured this sentinel to convert 121 // the exclusive range deletion end key into an inclusive key (reminder: [start, end)#seqnum 122 // is the form of a range deletion sentinel which can contribute a largest key = end#sentinel). 123 // In this case we don't return this as the earliest file since there is nothing actually 124 // equal to key in it. 125 126 m := l.files.SeekGE(l.cmp, key) 127 for m != nil { 128 largestKey := m.LargestRangeKey 129 if l.keyType == manifest.KeyTypePoint { 130 largestKey = m.LargestPointKey 131 } 132 if !largestKey.IsExclusiveSentinel() || l.cmp(largestKey.UserKey, key) != 0 { 133 break 134 } 135 m = l.files.Next() 136 } 137 return m 138 } 139 140 func (l *LevelIter) findFileLT(key []byte) *manifest.FileMetadata { 141 // Find the last file whose smallest key is < key. 142 return l.files.SeekLT(l.cmp, key) 143 } 144 145 type loadFileReturnIndicator int8 146 147 const ( 148 noFileLoaded loadFileReturnIndicator = iota 149 fileAlreadyLoaded 150 newFileLoaded 151 ) 152 153 func (l *LevelIter) loadFile(file *manifest.FileMetadata, dir int) loadFileReturnIndicator { 154 indicator := noFileLoaded 155 if l.iterFile == file { 156 if l.err != nil { 157 return noFileLoaded 158 } 159 if l.iter != nil { 160 // We are already at the file, but we would need to check for bounds. 161 // Set indicator accordingly. 162 indicator = fileAlreadyLoaded 163 } 164 // We were already at file, but don't have an iterator, probably because the file was 165 // beyond the iteration bounds. It may still be, but it is also possible that the bounds 166 // have changed. We handle that below. 167 } 168 169 // Note that LevelIter.Close() can be called multiple times. 170 if indicator != fileAlreadyLoaded { 171 if err := l.Close(); err != nil { 172 return noFileLoaded 173 } 174 } 175 176 l.iterFile = file 177 if file == nil { 178 return noFileLoaded 179 } 180 if indicator != fileAlreadyLoaded { 181 l.iter, l.err = l.newIter(file, &l.tableOpts) 182 indicator = newFileLoaded 183 } 184 if l.err != nil { 185 return noFileLoaded 186 } 187 return indicator 188 } 189 190 // SeekGE implements keyspan.FragmentIterator. 191 func (l *LevelIter) SeekGE(key []byte) *Span { 192 l.dir = +1 193 l.straddle = Span{} 194 l.straddleDir = 0 195 l.err = nil 196 197 f := l.findFileGE(key) 198 if f != nil && l.keyType == manifest.KeyTypeRange && l.cmp(key, f.SmallestRangeKey.UserKey) < 0 { 199 prevFile := l.files.Prev() 200 if prevFile != nil { 201 // We could unconditionally return an empty span between the seek key and 202 // f.SmallestRangeKey, however if this span is to the left of all range 203 // keys on this level, it could lead to inconsistent behaviour in relative 204 // positioning operations. Consider this example, with a b-c range key: 205 // 206 // SeekGE(a) -> a-b:{} 207 // Next() -> b-c{(#5,RANGEKEYSET,@4,foo)} 208 // Prev() -> nil 209 // 210 // Iterators higher up in the iterator stack rely on this sort of relative 211 // positioning consistency. 212 // 213 // TODO(bilal): Investigate ways to be able to return straddle spans in 214 // cases similar to the above, while still retaining correctness. 215 l.files.Next() 216 // Return a straddling key instead of loading the file. 217 l.iterFile = f 218 if err := l.Close(); err != nil { 219 return nil 220 } 221 l.straddleDir = +1 222 l.straddle = Span{ 223 Start: prevFile.LargestRangeKey.UserKey, 224 End: f.SmallestRangeKey.UserKey, 225 Keys: nil, 226 } 227 return &l.straddle 228 } 229 } 230 loadFileIndicator := l.loadFile(f, +1) 231 if loadFileIndicator == noFileLoaded { 232 return nil 233 } 234 if span := l.iter.SeekGE(key); span != nil { 235 return span 236 } 237 return l.skipEmptyFileForward() 238 } 239 240 // SeekLT implements keyspan.FragmentIterator. 241 func (l *LevelIter) SeekLT(key []byte) *Span { 242 l.dir = -1 243 l.straddle = Span{} 244 l.straddleDir = 0 245 l.err = nil 246 247 f := l.findFileLT(key) 248 if f != nil && l.keyType == manifest.KeyTypeRange && l.cmp(f.LargestRangeKey.UserKey, key) < 0 { 249 nextFile := l.files.Next() 250 if nextFile != nil { 251 // We could unconditionally return an empty span between f.LargestRangeKey 252 // and the seek key, however if this span is to the right of all range keys 253 // on this level, it could lead to inconsistent behaviour in relative 254 // positioning operations. Consider this example, with a b-c range key: 255 // 256 // SeekLT(d) -> c-d:{} 257 // Prev() -> b-c{(#5,RANGEKEYSET,@4,foo)} 258 // Next() -> nil 259 // 260 // Iterators higher up in the iterator stack rely on this sort of relative 261 // positioning consistency. 262 // 263 // TODO(bilal): Investigate ways to be able to return straddle spans in 264 // cases similar to the above, while still retaining correctness. 265 l.files.Prev() 266 // Return a straddling key instead of loading the file. 267 l.iterFile = f 268 if err := l.Close(); err != nil { 269 return nil 270 } 271 l.straddleDir = -1 272 l.straddle = Span{ 273 Start: f.LargestRangeKey.UserKey, 274 End: nextFile.SmallestRangeKey.UserKey, 275 Keys: nil, 276 } 277 return &l.straddle 278 } 279 } 280 if l.loadFile(l.findFileLT(key), -1) == noFileLoaded { 281 return nil 282 } 283 if span := l.iter.SeekLT(key); span != nil { 284 return span 285 } 286 return l.skipEmptyFileBackward() 287 } 288 289 // First implements keyspan.FragmentIterator. 290 func (l *LevelIter) First() *Span { 291 l.dir = +1 292 l.straddle = Span{} 293 l.straddleDir = 0 294 l.err = nil 295 296 if l.loadFile(l.files.First(), +1) == noFileLoaded { 297 return nil 298 } 299 if span := l.iter.First(); span != nil { 300 return span 301 } 302 return l.skipEmptyFileForward() 303 } 304 305 // Last implements keyspan.FragmentIterator. 306 func (l *LevelIter) Last() *Span { 307 l.dir = -1 308 l.straddle = Span{} 309 l.straddleDir = 0 310 l.err = nil 311 312 if l.loadFile(l.files.Last(), -1) == noFileLoaded { 313 return nil 314 } 315 if span := l.iter.Last(); span != nil { 316 return span 317 } 318 return l.skipEmptyFileBackward() 319 } 320 321 // Next implements keyspan.FragmentIterator. 322 func (l *LevelIter) Next() *Span { 323 if l.err != nil || (l.iter == nil && l.iterFile == nil && l.dir > 0) { 324 return nil 325 } 326 if l.iter == nil && l.iterFile == nil { 327 // l.dir <= 0 328 return l.First() 329 } 330 l.dir = +1 331 332 if l.iter != nil { 333 if span := l.iter.Next(); span != nil { 334 return span 335 } 336 } 337 return l.skipEmptyFileForward() 338 } 339 340 // Prev implements keyspan.FragmentIterator. 341 func (l *LevelIter) Prev() *Span { 342 if l.err != nil || (l.iter == nil && l.iterFile == nil && l.dir < 0) { 343 return nil 344 } 345 if l.iter == nil && l.iterFile == nil { 346 // l.dir >= 0 347 return l.Last() 348 } 349 l.dir = -1 350 351 if l.iter != nil { 352 if span := l.iter.Prev(); span != nil { 353 return span 354 } 355 } 356 return l.skipEmptyFileBackward() 357 } 358 359 func (l *LevelIter) skipEmptyFileForward() *Span { 360 if l.straddleDir == 0 && l.keyType == manifest.KeyTypeRange && 361 l.iterFile != nil && l.iter != nil { 362 // We were at a file that had spans. Check if the next file that has 363 // spans is not directly adjacent to the current file i.e. there is a 364 // gap in the span keyspace between the two files. In that case, synthesize 365 // a "straddle span" in l.straddle and return that. 366 // 367 // Straddle spans are not created in rangedel mode. 368 if err := l.Close(); err != nil { 369 l.err = err 370 return nil 371 } 372 startKey := l.iterFile.LargestRangeKey.UserKey 373 // Resetting l.iterFile without loading the file into l.iter is okay and 374 // does not change the logic in loadFile() as long as l.iter is also nil; 375 // which it should be due to the Close() call above. 376 l.iterFile = l.files.Next() 377 if l.iterFile == nil { 378 return nil 379 } 380 endKey := l.iterFile.SmallestRangeKey.UserKey 381 if l.cmp(startKey, endKey) < 0 { 382 // There is a gap between the two files. Synthesize a straddling span 383 // to avoid unnecessarily loading the next file. 384 l.straddle = Span{ 385 Start: startKey, 386 End: endKey, 387 } 388 l.straddleDir = +1 389 return &l.straddle 390 } 391 } else if l.straddleDir < 0 { 392 // We were at a straddle key, but are now changing directions. l.iterFile 393 // was already moved backward by skipEmptyFileBackward, so advance it 394 // forward. 395 l.iterFile = l.files.Next() 396 } 397 l.straddle = Span{} 398 l.straddleDir = 0 399 var span *Span 400 for span.Empty() { 401 fileToLoad := l.iterFile 402 if l.keyType == manifest.KeyTypePoint { 403 // We haven't iterated to the next file yet if we're in point key 404 // (rangedel) mode. 405 fileToLoad = l.files.Next() 406 } 407 if l.loadFile(fileToLoad, +1) == noFileLoaded { 408 return nil 409 } 410 span = l.iter.First() 411 // In rangedel mode, we can expect to get empty files that we'd need to 412 // skip over, but not in range key mode. 413 if l.keyType == manifest.KeyTypeRange { 414 break 415 } 416 } 417 return span 418 } 419 420 func (l *LevelIter) skipEmptyFileBackward() *Span { 421 // We were at a file that had spans. Check if the previous file that has 422 // spans is not directly adjacent to the current file i.e. there is a 423 // gap in the span keyspace between the two files. In that case, synthesize 424 // a "straddle span" in l.straddle and return that. 425 // 426 // Straddle spans are not created in rangedel mode. 427 if l.straddleDir == 0 && l.keyType == manifest.KeyTypeRange && 428 l.iterFile != nil && l.iter != nil { 429 if err := l.Close(); err != nil { 430 l.err = err 431 return nil 432 } 433 endKey := l.iterFile.SmallestRangeKey.UserKey 434 // Resetting l.iterFile without loading the file into l.iter is okay and 435 // does not change the logic in loadFile() as long as l.iter is also nil; 436 // which it should be due to the Close() call above. 437 l.iterFile = l.files.Prev() 438 if l.iterFile == nil { 439 return nil 440 } 441 startKey := l.iterFile.LargestRangeKey.UserKey 442 if l.cmp(startKey, endKey) < 0 { 443 // There is a gap between the two files. Synthesize a straddling span 444 // to avoid unnecessarily loading the next file. 445 l.straddle = Span{ 446 Start: startKey, 447 End: endKey, 448 } 449 l.straddleDir = -1 450 return &l.straddle 451 } 452 } else if l.straddleDir > 0 { 453 // We were at a straddle key, but are now changing directions. l.iterFile 454 // was already advanced forward by skipEmptyFileForward, so move it 455 // backward. 456 l.iterFile = l.files.Prev() 457 } 458 l.straddle = Span{} 459 l.straddleDir = 0 460 var span *Span 461 for span.Empty() { 462 fileToLoad := l.iterFile 463 if l.keyType == manifest.KeyTypePoint { 464 fileToLoad = l.files.Prev() 465 } 466 if l.loadFile(fileToLoad, -1) == noFileLoaded { 467 return nil 468 } 469 span = l.iter.Last() 470 // In rangedel mode, we can expect to get empty files that we'd need to 471 // skip over, but not in range key mode as the filter on the FileMetadata 472 // should guarantee we always get a non-empty file. 473 if l.keyType == manifest.KeyTypeRange { 474 break 475 } 476 } 477 return span 478 } 479 480 // Error implements keyspan.FragmentIterator. 481 func (l *LevelIter) Error() error { 482 if l.err != nil || l.iter == nil { 483 return l.err 484 } 485 return l.iter.Error() 486 } 487 488 // Close implements keyspan.FragmentIterator. 489 func (l *LevelIter) Close() error { 490 if l.iter != nil { 491 l.err = l.iter.Close() 492 l.iter = nil 493 } 494 return l.err 495 } 496 497 // String implements keyspan.FragmentIterator. 498 func (l *LevelIter) String() string { 499 if l.iterFile != nil { 500 return fmt.Sprintf("%s: fileNum=%s", l.level, l.iterFile.FileNum) 501 } 502 return fmt.Sprintf("%s: fileNum=<nil>", l.level) 503 }