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  }