github.com/zuoyebang/bitalostable@v1.0.1-0.20240229032404-e3b99a834294/level_iter_test.go (about)

     1  // Copyright 2018 The LevelDB-Go and Pebble and Bitalostored 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 bitalostable
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"strings"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/stretchr/testify/require"
    15  	"github.com/zuoyebang/bitalostable/bloom"
    16  	"github.com/zuoyebang/bitalostable/internal/base"
    17  	"github.com/zuoyebang/bitalostable/internal/datadriven"
    18  	"github.com/zuoyebang/bitalostable/internal/keyspan"
    19  	"github.com/zuoyebang/bitalostable/internal/manifest"
    20  	"github.com/zuoyebang/bitalostable/internal/rangedel"
    21  	"github.com/zuoyebang/bitalostable/sstable"
    22  	"github.com/zuoyebang/bitalostable/vfs"
    23  	"golang.org/x/exp/rand"
    24  )
    25  
    26  const (
    27  	level = 1
    28  )
    29  
    30  func TestLevelIter(t *testing.T) {
    31  	var iters []*fakeIter
    32  	var files manifest.LevelSlice
    33  
    34  	newIters := func(
    35  		file *manifest.FileMetadata, opts *IterOptions, _ internalIterOpts,
    36  	) (internalIterator, keyspan.FragmentIterator, error) {
    37  		f := *iters[file.FileNum]
    38  		f.lower = opts.GetLowerBound()
    39  		f.upper = opts.GetUpperBound()
    40  		return &f, nil, nil
    41  	}
    42  
    43  	datadriven.RunTest(t, "testdata/level_iter", func(d *datadriven.TestData) string {
    44  		switch d.Cmd {
    45  		case "define":
    46  			iters = nil
    47  			var metas []*fileMetadata
    48  			for _, line := range strings.Split(d.Input, "\n") {
    49  				f := &fakeIter{}
    50  				for _, key := range strings.Fields(line) {
    51  					j := strings.Index(key, ":")
    52  					f.keys = append(f.keys, base.ParseInternalKey(key[:j]))
    53  					f.vals = append(f.vals, []byte(key[j+1:]))
    54  				}
    55  				iters = append(iters, f)
    56  
    57  				meta := (&fileMetadata{
    58  					FileNum: FileNum(len(metas)),
    59  				}).ExtendPointKeyBounds(
    60  					DefaultComparer.Compare,
    61  					f.keys[0],
    62  					f.keys[len(f.keys)-1],
    63  				)
    64  				metas = append(metas, meta)
    65  			}
    66  			files = manifest.NewLevelSliceKeySorted(base.DefaultComparer.Compare, metas)
    67  
    68  			return ""
    69  
    70  		case "iter":
    71  			var opts IterOptions
    72  			for _, arg := range d.CmdArgs {
    73  				if len(arg.Vals) != 1 {
    74  					return fmt.Sprintf("%s: %s=<value>", d.Cmd, arg.Key)
    75  				}
    76  				switch arg.Key {
    77  				case "lower":
    78  					opts.LowerBound = []byte(arg.Vals[0])
    79  				case "upper":
    80  					opts.UpperBound = []byte(arg.Vals[0])
    81  				default:
    82  					return fmt.Sprintf("%s: unknown arg: %s", d.Cmd, arg.Key)
    83  				}
    84  			}
    85  
    86  			iter := newLevelIter(opts, DefaultComparer.Compare,
    87  				func(a []byte) int { return len(a) }, newIters, files.Iter(), manifest.Level(level),
    88  				nil)
    89  			defer iter.Close()
    90  			// Fake up the range deletion initialization.
    91  			iter.initRangeDel(new(keyspan.FragmentIterator))
    92  			iter.disableInvariants = true
    93  			return runInternalIterCmd(d, iter, iterCmdVerboseKey)
    94  
    95  		case "load":
    96  			// The "load" command allows testing the iterator options passed to load
    97  			// sstables.
    98  			//
    99  			// load <key> [lower=<key>] [upper=<key>]
   100  			var opts IterOptions
   101  			var key string
   102  			for _, arg := range d.CmdArgs {
   103  				if len(arg.Vals) == 0 {
   104  					key = arg.Key
   105  					continue
   106  				}
   107  				if len(arg.Vals) != 1 {
   108  					return fmt.Sprintf("%s: %s=<value>", d.Cmd, arg.Key)
   109  				}
   110  				switch arg.Key {
   111  				case "lower":
   112  					opts.LowerBound = []byte(arg.Vals[0])
   113  				case "upper":
   114  					opts.UpperBound = []byte(arg.Vals[0])
   115  				default:
   116  					return fmt.Sprintf("%s: unknown arg: %s", d.Cmd, arg.Key)
   117  				}
   118  			}
   119  
   120  			var tableOpts *IterOptions
   121  			newIters2 := func(
   122  				file *manifest.FileMetadata, opts *IterOptions, internalOpts internalIterOpts,
   123  			) (internalIterator, keyspan.FragmentIterator, error) {
   124  				tableOpts = opts
   125  				return newIters(file, opts, internalOpts)
   126  			}
   127  
   128  			iter := newLevelIter(opts, DefaultComparer.Compare,
   129  				func(a []byte) int { return len(a) }, newIters2, files.Iter(),
   130  				manifest.Level(level), nil)
   131  			iter.SeekGE([]byte(key), base.SeekGEFlagsNone)
   132  			lower, upper := tableOpts.GetLowerBound(), tableOpts.GetUpperBound()
   133  			return fmt.Sprintf("[%s,%s]\n", lower, upper)
   134  
   135  		default:
   136  			return fmt.Sprintf("unknown command: %s", d.Cmd)
   137  		}
   138  	})
   139  }
   140  
   141  type levelIterTest struct {
   142  	cmp          base.Comparer
   143  	mem          vfs.FS
   144  	readers      []*sstable.Reader
   145  	metas        []*fileMetadata
   146  	itersCreated int
   147  }
   148  
   149  func newLevelIterTest() *levelIterTest {
   150  	lt := &levelIterTest{
   151  		cmp: *DefaultComparer,
   152  		mem: vfs.NewMem(),
   153  	}
   154  	lt.cmp.Split = func(a []byte) int { return len(a) }
   155  	return lt
   156  }
   157  
   158  func (lt *levelIterTest) newIters(
   159  	file *manifest.FileMetadata, opts *IterOptions, iio internalIterOpts,
   160  ) (internalIterator, keyspan.FragmentIterator, error) {
   161  	lt.itersCreated++
   162  	iter, err := lt.readers[file.FileNum].NewIterWithBlockPropertyFilters(opts.LowerBound, opts.UpperBound, nil, true, iio.stats)
   163  	if err != nil {
   164  		return nil, nil, err
   165  	}
   166  	rangeDelIter, err := lt.readers[file.FileNum].NewRawRangeDelIter()
   167  	if err != nil {
   168  		return nil, nil, err
   169  	}
   170  	return iter, rangeDelIter, nil
   171  }
   172  
   173  func (lt *levelIterTest) runClear(d *datadriven.TestData) string {
   174  	lt.mem = vfs.NewMem()
   175  	for _, r := range lt.readers {
   176  		r.Close()
   177  	}
   178  	lt.readers = nil
   179  	lt.metas = nil
   180  	lt.itersCreated = 0
   181  	return ""
   182  }
   183  
   184  func (lt *levelIterTest) runBuild(d *datadriven.TestData) string {
   185  	fileNum := FileNum(len(lt.readers))
   186  	name := fmt.Sprint(fileNum)
   187  	f0, err := lt.mem.Create(name)
   188  	if err != nil {
   189  		return err.Error()
   190  	}
   191  
   192  	tableFormat := sstable.TableFormatRocksDBv2
   193  	for _, arg := range d.CmdArgs {
   194  		if arg.Key == "format" {
   195  			switch arg.Vals[0] {
   196  			case "rocksdbv2":
   197  				tableFormat = sstable.TableFormatRocksDBv2
   198  			case "bitalostablev2":
   199  				tableFormat = sstable.TableFormatPebblev2
   200  			}
   201  		}
   202  	}
   203  	fp := bloom.FilterPolicy(10)
   204  	w := sstable.NewWriter(f0, sstable.WriterOptions{
   205  		Comparer:     &lt.cmp,
   206  		FilterPolicy: fp,
   207  		TableFormat:  tableFormat,
   208  	})
   209  	var tombstones []keyspan.Span
   210  	f := keyspan.Fragmenter{
   211  		Cmp:    lt.cmp.Compare,
   212  		Format: lt.cmp.FormatKey,
   213  		Emit: func(fragmented keyspan.Span) {
   214  			tombstones = append(tombstones, fragmented)
   215  		},
   216  	}
   217  	for _, key := range strings.Split(d.Input, "\n") {
   218  		j := strings.Index(key, ":")
   219  		ikey := base.ParseInternalKey(key[:j])
   220  		value := []byte(key[j+1:])
   221  		switch ikey.Kind() {
   222  		case InternalKeyKindRangeDelete:
   223  			f.Add(rangedel.Decode(ikey, value, nil))
   224  		case InternalKeyKindRangeKeySet, InternalKeyKindRangeKeyUnset, InternalKeyKindRangeKeyDelete:
   225  			if err := w.AddRangeKey(ikey, value); err != nil {
   226  				return err.Error()
   227  			}
   228  		default:
   229  			if err := w.Add(ikey, value); err != nil {
   230  				return err.Error()
   231  			}
   232  		}
   233  	}
   234  	f.Finish()
   235  	for _, v := range tombstones {
   236  		if err := rangedel.Encode(&v, w.Add); err != nil {
   237  			return err.Error()
   238  		}
   239  	}
   240  	if err := w.Close(); err != nil {
   241  		return err.Error()
   242  	}
   243  	meta, err := w.Metadata()
   244  	if err != nil {
   245  		return err.Error()
   246  	}
   247  
   248  	f1, err := lt.mem.Open(name)
   249  	if err != nil {
   250  		return err.Error()
   251  	}
   252  	r, err := sstable.NewReader(f1, sstable.ReaderOptions{
   253  		Filters: map[string]FilterPolicy{
   254  			fp.Name(): fp,
   255  		},
   256  	})
   257  	if err != nil {
   258  		return err.Error()
   259  	}
   260  	lt.readers = append(lt.readers, r)
   261  	m := &fileMetadata{FileNum: fileNum}
   262  	if meta.HasPointKeys {
   263  		m.ExtendPointKeyBounds(lt.cmp.Compare, meta.SmallestPoint, meta.LargestPoint)
   264  	}
   265  	if meta.HasRangeDelKeys {
   266  		m.ExtendPointKeyBounds(lt.cmp.Compare, meta.SmallestRangeDel, meta.LargestRangeDel)
   267  	}
   268  	if meta.HasRangeKeys {
   269  		m.ExtendRangeKeyBounds(lt.cmp.Compare, meta.SmallestRangeKey, meta.LargestRangeKey)
   270  	}
   271  	lt.metas = append(lt.metas, m)
   272  
   273  	var buf bytes.Buffer
   274  	for _, f := range lt.metas {
   275  		fmt.Fprintf(&buf, "%d: %s-%s\n", f.FileNum, f.Smallest, f.Largest)
   276  	}
   277  	return buf.String()
   278  }
   279  
   280  func TestLevelIterBoundaries(t *testing.T) {
   281  	lt := newLevelIterTest()
   282  	defer lt.runClear(nil)
   283  
   284  	var iter *levelIter
   285  	datadriven.RunTest(t, "testdata/level_iter_boundaries", func(d *datadriven.TestData) string {
   286  		switch d.Cmd {
   287  		case "clear":
   288  			return lt.runClear(d)
   289  
   290  		case "build":
   291  			return lt.runBuild(d)
   292  
   293  		case "iter":
   294  			// The save and continue parameters allow us to save the iterator
   295  			// for later continued use.
   296  			save := false
   297  			cont := false
   298  			for _, arg := range d.CmdArgs {
   299  				switch arg.Key {
   300  				case "save":
   301  					save = true
   302  				case "continue":
   303  					cont = true
   304  				default:
   305  					return fmt.Sprintf("%s: unknown arg: %s", d.Cmd, arg.Key)
   306  				}
   307  			}
   308  			if !cont && iter != nil {
   309  				return "preceding iter was not closed"
   310  			}
   311  			if cont && iter == nil {
   312  				return "no existing iter"
   313  			}
   314  			if iter == nil {
   315  				slice := manifest.NewLevelSliceKeySorted(lt.cmp.Compare, lt.metas)
   316  				iter = newLevelIter(IterOptions{}, DefaultComparer.Compare,
   317  					func(a []byte) int { return len(a) }, lt.newIters, slice.Iter(),
   318  					manifest.Level(level), nil)
   319  				// Fake up the range deletion initialization.
   320  				iter.initRangeDel(new(keyspan.FragmentIterator))
   321  			}
   322  			if !save {
   323  				defer func() {
   324  					iter.Close()
   325  					iter = nil
   326  				}()
   327  			}
   328  			return runInternalIterCmd(d, iter, iterCmdVerboseKey)
   329  
   330  		case "file-pos":
   331  			// Returns the FileNum at which the iterator is positioned.
   332  			if iter == nil {
   333  				return "nil levelIter"
   334  			}
   335  			if iter.iterFile == nil {
   336  				return "nil iterFile"
   337  			}
   338  			return fmt.Sprintf("file %d", iter.iterFile.FileNum)
   339  
   340  		default:
   341  			return fmt.Sprintf("unknown command: %s", d.Cmd)
   342  		}
   343  	})
   344  }
   345  
   346  // levelIterTestIter allows a datadriven test to use runInternalIterCmd and
   347  // perform parallel operations on both both a levelIter and rangeDelIter.
   348  type levelIterTestIter struct {
   349  	*levelIter
   350  	rangeDelIter keyspan.FragmentIterator
   351  }
   352  
   353  func (i *levelIterTestIter) rangeDelSeek(
   354  	key []byte, ikey *InternalKey, val []byte, dir int,
   355  ) (*InternalKey, []byte) {
   356  	var tombstone keyspan.Span
   357  	if i.rangeDelIter != nil {
   358  		var t *keyspan.Span
   359  		if dir < 0 {
   360  			t = keyspan.SeekLE(i.levelIter.cmp, i.rangeDelIter, key)
   361  		} else {
   362  			t = keyspan.SeekGE(i.levelIter.cmp, i.rangeDelIter, key)
   363  		}
   364  		if t != nil {
   365  			tombstone = t.Visible(1000)
   366  		}
   367  	}
   368  	if ikey == nil {
   369  		return &InternalKey{
   370  			UserKey: []byte(fmt.Sprintf("./%s", tombstone)),
   371  		}, nil
   372  	}
   373  	return &InternalKey{
   374  		UserKey: []byte(fmt.Sprintf("%s/%s", ikey.UserKey, tombstone)),
   375  		Trailer: ikey.Trailer,
   376  	}, val
   377  }
   378  
   379  func (i *levelIterTestIter) String() string {
   380  	return "level-iter-test"
   381  }
   382  
   383  func (i *levelIterTestIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey, []byte) {
   384  	ikey, val := i.levelIter.SeekGE(key, flags)
   385  	return i.rangeDelSeek(key, ikey, val, 1)
   386  }
   387  
   388  func (i *levelIterTestIter) SeekPrefixGE(
   389  	prefix, key []byte, flags base.SeekGEFlags,
   390  ) (*base.InternalKey, []byte) {
   391  	ikey, val := i.levelIter.SeekPrefixGE(prefix, key, flags)
   392  	return i.rangeDelSeek(key, ikey, val, 1)
   393  }
   394  
   395  func (i *levelIterTestIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey, []byte) {
   396  	ikey, val := i.levelIter.SeekLT(key, flags)
   397  	return i.rangeDelSeek(key, ikey, val, -1)
   398  }
   399  
   400  func TestLevelIterSeek(t *testing.T) {
   401  	lt := newLevelIterTest()
   402  	defer lt.runClear(nil)
   403  
   404  	datadriven.RunTest(t, "testdata/level_iter_seek", func(d *datadriven.TestData) string {
   405  		switch d.Cmd {
   406  		case "clear":
   407  			return lt.runClear(d)
   408  
   409  		case "build":
   410  			return lt.runBuild(d)
   411  
   412  		case "iter":
   413  			var stats base.InternalIteratorStats
   414  			slice := manifest.NewLevelSliceKeySorted(lt.cmp.Compare, lt.metas)
   415  			iter := &levelIterTestIter{levelIter: &levelIter{}}
   416  			iter.init(IterOptions{}, DefaultComparer.Compare,
   417  				func(a []byte) int { return len(a) }, lt.newIters, slice.Iter(),
   418  				manifest.Level(level), internalIterOpts{stats: &stats})
   419  			defer iter.Close()
   420  			iter.initRangeDel(&iter.rangeDelIter)
   421  			return runInternalIterCmd(d, iter, iterCmdVerboseKey, iterCmdStats(&stats))
   422  
   423  		case "iters-created":
   424  			return fmt.Sprintf("%d", lt.itersCreated)
   425  		default:
   426  			return fmt.Sprintf("unknown command: %s", d.Cmd)
   427  		}
   428  	})
   429  }
   430  
   431  func buildLevelIterTables(
   432  	b *testing.B, blockSize, restartInterval, count int,
   433  ) ([]*sstable.Reader, manifest.LevelSlice, [][]byte, func()) {
   434  	mem := vfs.NewMem()
   435  	files := make([]vfs.File, count)
   436  	for i := range files {
   437  		f, err := mem.Create(fmt.Sprintf("bench%d", i))
   438  		if err != nil {
   439  			b.Fatal(err)
   440  		}
   441  		files[i] = f
   442  	}
   443  
   444  	writers := make([]*sstable.Writer, len(files))
   445  	for i := range files {
   446  		writers[i] = sstable.NewWriter(files[i], sstable.WriterOptions{
   447  			BlockRestartInterval: restartInterval,
   448  			BlockSize:            blockSize,
   449  			Compression:          NoCompression,
   450  		})
   451  	}
   452  
   453  	var keys [][]byte
   454  	var i int
   455  	const targetSize = 2 << 20
   456  	for _, w := range writers {
   457  		for ; w.EstimatedSize() < targetSize; i++ {
   458  			key := []byte(fmt.Sprintf("%08d", i))
   459  			keys = append(keys, key)
   460  			ikey := base.MakeInternalKey(key, 0, InternalKeyKindSet)
   461  			w.Add(ikey, nil)
   462  		}
   463  		if err := w.Close(); err != nil {
   464  			b.Fatal(err)
   465  		}
   466  	}
   467  
   468  	opts := sstable.ReaderOptions{Cache: NewCache(128 << 20)}
   469  	defer opts.Cache.Unref()
   470  	readers := make([]*sstable.Reader, len(files))
   471  	for i := range files {
   472  		f, err := mem.Open(fmt.Sprintf("bench%d", i))
   473  		if err != nil {
   474  			b.Fatal(err)
   475  		}
   476  		readers[i], err = sstable.NewReader(f, opts)
   477  		if err != nil {
   478  			b.Fatal(err)
   479  		}
   480  	}
   481  
   482  	cleanup := func() {
   483  		for _, r := range readers {
   484  			require.NoError(b, r.Close())
   485  		}
   486  	}
   487  
   488  	meta := make([]*fileMetadata, len(readers))
   489  	for i := range readers {
   490  		iter, err := readers[i].NewIter(nil /* lower */, nil /* upper */)
   491  		require.NoError(b, err)
   492  		smallest, _ := iter.First()
   493  		meta[i] = &fileMetadata{}
   494  		meta[i].FileNum = FileNum(i)
   495  		largest, _ := iter.Last()
   496  		meta[i].ExtendPointKeyBounds(opts.Comparer.Compare, (*smallest).Clone(), (*largest).Clone())
   497  	}
   498  	slice := manifest.NewLevelSliceKeySorted(base.DefaultComparer.Compare, meta)
   499  	return readers, slice, keys, cleanup
   500  }
   501  
   502  func BenchmarkLevelIterSeekGE(b *testing.B) {
   503  	const blockSize = 32 << 10
   504  
   505  	for _, restartInterval := range []int{16} {
   506  		b.Run(fmt.Sprintf("restart=%d", restartInterval),
   507  			func(b *testing.B) {
   508  				for _, count := range []int{5} {
   509  					b.Run(fmt.Sprintf("count=%d", count),
   510  						func(b *testing.B) {
   511  							readers, metas, keys, cleanup := buildLevelIterTables(b, blockSize, restartInterval, count)
   512  							defer cleanup()
   513  							newIters := func(
   514  								file *manifest.FileMetadata, _ *IterOptions, _ internalIterOpts,
   515  							) (internalIterator, keyspan.FragmentIterator, error) {
   516  								iter, err := readers[file.FileNum].NewIter(nil /* lower */, nil /* upper */)
   517  								return iter, nil, err
   518  							}
   519  							l := newLevelIter(IterOptions{}, DefaultComparer.Compare, nil, newIters, metas.Iter(), manifest.Level(level), nil)
   520  							rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano())))
   521  
   522  							b.ResetTimer()
   523  							for i := 0; i < b.N; i++ {
   524  								l.SeekGE(keys[rng.Intn(len(keys))], base.SeekGEFlagsNone)
   525  							}
   526  							l.Close()
   527  						})
   528  				}
   529  			})
   530  	}
   531  }
   532  
   533  // A benchmark that simulates the behavior of a levelIter being used as part
   534  // of a mergingIter where narrow bounds are repeatedly set and used to Seek
   535  // and then iterate over the keys within the bounds. This resembles MVCC
   536  // scanning by CockroachDB when doing a lookup/index join with a large number
   537  // of left rows, that are batched and reuse the same iterator, and which can
   538  // have good locality of access. This results in the successive bounds being
   539  // in the same file.
   540  func BenchmarkLevelIterSeqSeekGEWithBounds(b *testing.B) {
   541  	const blockSize = 32 << 10
   542  
   543  	for _, restartInterval := range []int{16} {
   544  		b.Run(fmt.Sprintf("restart=%d", restartInterval),
   545  			func(b *testing.B) {
   546  				for _, count := range []int{5} {
   547  					b.Run(fmt.Sprintf("count=%d", count),
   548  						func(b *testing.B) {
   549  							readers, metas, keys, cleanup :=
   550  								buildLevelIterTables(b, blockSize, restartInterval, count)
   551  							defer cleanup()
   552  							// This newIters is cheaper than in practice since it does not do
   553  							// tableCacheShard.findNode.
   554  							newIters := func(
   555  								file *manifest.FileMetadata, opts *IterOptions, _ internalIterOpts,
   556  							) (internalIterator, keyspan.FragmentIterator, error) {
   557  								iter, err := readers[file.FileNum].NewIter(
   558  									opts.LowerBound, opts.UpperBound)
   559  								return iter, nil, err
   560  							}
   561  							l := newLevelIter(IterOptions{}, DefaultComparer.Compare, nil, newIters, metas.Iter(), manifest.Level(level), nil)
   562  							// Fake up the range deletion initialization, to resemble the usage
   563  							// in a mergingIter.
   564  							l.initRangeDel(new(keyspan.FragmentIterator))
   565  							keyCount := len(keys)
   566  							b.ResetTimer()
   567  							for i := 0; i < b.N; i++ {
   568  								pos := i % (keyCount - 1)
   569  								l.SetBounds(keys[pos], keys[pos+1])
   570  								// SeekGE will return keys[pos].
   571  								k, _ := l.SeekGE(keys[pos], base.SeekGEFlagsNone)
   572  								// Next() will get called once and return nil.
   573  								for k != nil {
   574  									k, _ = l.Next()
   575  								}
   576  							}
   577  							l.Close()
   578  						})
   579  				}
   580  			})
   581  	}
   582  }
   583  
   584  // BenchmarkLevelIterSeqSeekPrefixGE simulates the behavior of a levelIter
   585  // being used as part of a mergingIter where SeekPrefixGE is used to seek in a
   586  // monotonically increasing manner. This resembles key-value lookups done by
   587  // CockroachDB when evaluating Put operations.
   588  func BenchmarkLevelIterSeqSeekPrefixGE(b *testing.B) {
   589  	const blockSize = 32 << 10
   590  	const restartInterval = 16
   591  	readers, metas, keys, cleanup :=
   592  		buildLevelIterTables(b, blockSize, restartInterval, 5)
   593  	defer cleanup()
   594  	// This newIters is cheaper than in practice since it does not do
   595  	// tableCacheShard.findNode.
   596  	newIters := func(
   597  		file *manifest.FileMetadata, opts *IterOptions, _ internalIterOpts,
   598  	) (internalIterator, keyspan.FragmentIterator, error) {
   599  		iter, err := readers[file.FileNum].NewIter(
   600  			opts.LowerBound, opts.UpperBound)
   601  		return iter, nil, err
   602  	}
   603  
   604  	for _, skip := range []int{1, 2, 4, 8, 16} {
   605  		for _, useNext := range []bool{false, true} {
   606  			b.Run(fmt.Sprintf("skip=%d/use-next=%t", skip, useNext),
   607  				func(b *testing.B) {
   608  					l := newLevelIter(IterOptions{}, DefaultComparer.Compare,
   609  						func(a []byte) int { return len(a) }, newIters, metas.Iter(),
   610  						manifest.Level(level), nil)
   611  					// Fake up the range deletion initialization, to resemble the usage
   612  					// in a mergingIter.
   613  					l.initRangeDel(new(keyspan.FragmentIterator))
   614  					keyCount := len(keys)
   615  					pos := 0
   616  					l.SeekPrefixGE(keys[pos], keys[pos], base.SeekGEFlagsNone)
   617  					b.ResetTimer()
   618  					for i := 0; i < b.N; i++ {
   619  						pos += skip
   620  						var flags base.SeekGEFlags
   621  						if useNext {
   622  							flags = flags.EnableTrySeekUsingNext()
   623  						}
   624  						if pos >= keyCount {
   625  							pos = 0
   626  							flags = flags.DisableTrySeekUsingNext()
   627  						}
   628  						// SeekPrefixGE will return keys[pos].
   629  						l.SeekPrefixGE(keys[pos], keys[pos], flags)
   630  					}
   631  					b.StopTimer()
   632  					l.Close()
   633  				})
   634  		}
   635  	}
   636  }
   637  
   638  func BenchmarkLevelIterNext(b *testing.B) {
   639  	const blockSize = 32 << 10
   640  
   641  	for _, restartInterval := range []int{16} {
   642  		b.Run(fmt.Sprintf("restart=%d", restartInterval),
   643  			func(b *testing.B) {
   644  				for _, count := range []int{5} {
   645  					b.Run(fmt.Sprintf("count=%d", count),
   646  						func(b *testing.B) {
   647  							readers, metas, _, cleanup := buildLevelIterTables(b, blockSize, restartInterval, count)
   648  							defer cleanup()
   649  							newIters := func(
   650  								file *manifest.FileMetadata, _ *IterOptions, _ internalIterOpts,
   651  							) (internalIterator, keyspan.FragmentIterator, error) {
   652  								iter, err := readers[file.FileNum].NewIter(nil /* lower */, nil /* upper */)
   653  								return iter, nil, err
   654  							}
   655  							l := newLevelIter(IterOptions{}, DefaultComparer.Compare, nil, newIters, metas.Iter(), manifest.Level(level), nil)
   656  
   657  							b.ResetTimer()
   658  							for i := 0; i < b.N; i++ {
   659  								key, _ := l.Next()
   660  								if key == nil {
   661  									key, _ = l.First()
   662  								}
   663  								_ = key
   664  							}
   665  							l.Close()
   666  						})
   667  				}
   668  			})
   669  	}
   670  }
   671  
   672  func BenchmarkLevelIterPrev(b *testing.B) {
   673  	const blockSize = 32 << 10
   674  
   675  	for _, restartInterval := range []int{16} {
   676  		b.Run(fmt.Sprintf("restart=%d", restartInterval),
   677  			func(b *testing.B) {
   678  				for _, count := range []int{5} {
   679  					b.Run(fmt.Sprintf("count=%d", count),
   680  						func(b *testing.B) {
   681  							readers, metas, _, cleanup := buildLevelIterTables(b, blockSize, restartInterval, count)
   682  							defer cleanup()
   683  							newIters := func(
   684  								file *manifest.FileMetadata, _ *IterOptions, _ internalIterOpts,
   685  							) (internalIterator, keyspan.FragmentIterator, error) {
   686  								iter, err := readers[file.FileNum].NewIter(nil /* lower */, nil /* upper */)
   687  								return iter, nil, err
   688  							}
   689  							l := newLevelIter(IterOptions{}, DefaultComparer.Compare, nil, newIters, metas.Iter(), manifest.Level(level), nil)
   690  
   691  							b.ResetTimer()
   692  							for i := 0; i < b.N; i++ {
   693  								key, _ := l.Prev()
   694  								if key == nil {
   695  									key, _ = l.Last()
   696  								}
   697  								_ = key
   698  							}
   699  							l.Close()
   700  						})
   701  				}
   702  			})
   703  	}
   704  }