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

     1  // Copyright 2020 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 manifest
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"io"
    11  	"math"
    12  	"os"
    13  	"sort"
    14  	"strconv"
    15  	"strings"
    16  	"testing"
    17  	"time"
    18  
    19  	"github.com/stretchr/testify/require"
    20  	"github.com/zuoyebang/bitalostable/internal/base"
    21  	"github.com/zuoyebang/bitalostable/internal/datadriven"
    22  	"github.com/zuoyebang/bitalostable/internal/testkeys"
    23  	"github.com/zuoyebang/bitalostable/record"
    24  	"golang.org/x/exp/rand"
    25  )
    26  
    27  func readManifest(filename string) (*Version, error) {
    28  	f, err := os.Open(filename)
    29  	if err != nil {
    30  		return nil, err
    31  	}
    32  	defer f.Close()
    33  	rr := record.NewReader(f, 0 /* logNum */)
    34  	var v *Version
    35  	addedByFileNum := make(map[base.FileNum]*FileMetadata)
    36  	for {
    37  		r, err := rr.Next()
    38  		if err == io.EOF {
    39  			break
    40  		}
    41  		if err != nil {
    42  			return nil, err
    43  		}
    44  		var ve VersionEdit
    45  		if err = ve.Decode(r); err != nil {
    46  			return nil, err
    47  		}
    48  		var bve BulkVersionEdit
    49  		bve.AddedByFileNum = addedByFileNum
    50  		if err := bve.Accumulate(&ve); err != nil {
    51  			return nil, err
    52  		}
    53  		if v, _, err = bve.Apply(v, base.DefaultComparer.Compare, base.DefaultFormatter, 10<<20, 32000); err != nil {
    54  			return nil, err
    55  		}
    56  	}
    57  	return v, nil
    58  }
    59  
    60  func TestL0Sublevels_LargeImportL0(t *testing.T) {
    61  	// TODO(bilal): Fix this test.
    62  	t.Skip()
    63  	v, err := readManifest("testdata/MANIFEST_import")
    64  	require.NoError(t, err)
    65  
    66  	sublevels, err := NewL0Sublevels(&v.Levels[0],
    67  		base.DefaultComparer.Compare, base.DefaultFormatter, 5<<20)
    68  	require.NoError(t, err)
    69  	fmt.Printf("L0Sublevels:\n%s\n\n", sublevels)
    70  
    71  	for i := 0; ; i++ {
    72  		c, err := sublevels.PickBaseCompaction(2, LevelSlice{})
    73  		require.NoError(t, err)
    74  		if c == nil {
    75  			break
    76  		}
    77  		fmt.Printf("%d: base compaction: filecount: %d, bytes: %d, interval: [%d, %d], seed depth: %d\n",
    78  			i, len(c.Files), c.fileBytes, c.minIntervalIndex, c.maxIntervalIndex, c.seedIntervalStackDepthReduction)
    79  		var files []*FileMetadata
    80  		for i := range c.Files {
    81  			if c.FilesIncluded[i] {
    82  				c.Files[i].CompactionState = CompactionStateCompacting
    83  				files = append(files, c.Files[i])
    84  			}
    85  		}
    86  		require.NoError(t, sublevels.UpdateStateForStartedCompaction(
    87  			[]LevelSlice{NewLevelSliceSeqSorted(files)}, true))
    88  	}
    89  
    90  	for i := 0; ; i++ {
    91  		c, err := sublevels.PickIntraL0Compaction(math.MaxUint64, 2)
    92  		require.NoError(t, err)
    93  		if c == nil {
    94  			break
    95  		}
    96  		fmt.Printf("%d: intra-L0 compaction: filecount: %d, bytes: %d, interval: [%d, %d], seed depth: %d\n",
    97  			i, len(c.Files), c.fileBytes, c.minIntervalIndex, c.maxIntervalIndex, c.seedIntervalStackDepthReduction)
    98  		var files []*FileMetadata
    99  		for i := range c.Files {
   100  			if c.FilesIncluded[i] {
   101  				c.Files[i].CompactionState = CompactionStateCompacting
   102  				c.Files[i].IsIntraL0Compacting = true
   103  				files = append(files, c.Files[i])
   104  			}
   105  		}
   106  		require.NoError(t, sublevels.UpdateStateForStartedCompaction(
   107  			[]LevelSlice{NewLevelSliceSeqSorted(files)}, false))
   108  	}
   109  }
   110  
   111  func visualizeSublevels(
   112  	s *L0Sublevels, compactionFiles bitSet, otherLevels [][]*FileMetadata,
   113  ) string {
   114  	var buf strings.Builder
   115  	if compactionFiles == nil {
   116  		compactionFiles = newBitSet(s.levelMetadata.Len())
   117  	}
   118  	largestChar := byte('a')
   119  	printLevel := func(files []*FileMetadata, level string, isL0 bool) {
   120  		lastChar := byte('a')
   121  		fmt.Fprintf(&buf, "L%s:", level)
   122  		for i := 0; i < 5-len(level); i++ {
   123  			buf.WriteByte(' ')
   124  		}
   125  		for j, f := range files {
   126  			for lastChar < f.Smallest.UserKey[0] {
   127  				buf.WriteString("   ")
   128  				lastChar++
   129  			}
   130  			buf.WriteByte(f.Smallest.UserKey[0])
   131  			middleChar := byte('-')
   132  			if isL0 {
   133  				if compactionFiles[f.L0Index] {
   134  					middleChar = '+'
   135  				} else if f.IsCompacting() {
   136  					if f.IsIntraL0Compacting {
   137  						middleChar = '^'
   138  					} else {
   139  						middleChar = 'v'
   140  					}
   141  				}
   142  			} else if f.IsCompacting() {
   143  				middleChar = '='
   144  			}
   145  			if largestChar < f.Largest.UserKey[0] {
   146  				largestChar = f.Largest.UserKey[0]
   147  			}
   148  			if f.Smallest.UserKey[0] == f.Largest.UserKey[0] {
   149  				buf.WriteByte(f.Largest.UserKey[0])
   150  				if compactionFiles[f.L0Index] {
   151  					buf.WriteByte('+')
   152  				} else if j < len(files)-1 {
   153  					buf.WriteByte(' ')
   154  				}
   155  				lastChar++
   156  				continue
   157  			}
   158  			buf.WriteByte(middleChar)
   159  			buf.WriteByte(middleChar)
   160  			lastChar++
   161  			for lastChar < f.Largest.UserKey[0] {
   162  				buf.WriteByte(middleChar)
   163  				buf.WriteByte(middleChar)
   164  				buf.WriteByte(middleChar)
   165  				lastChar++
   166  			}
   167  			if f.Largest.IsExclusiveSentinel() &&
   168  				j < len(files)-1 && files[j+1].Smallest.UserKey[0] == f.Largest.UserKey[0] {
   169  				// This case happens where two successive files have
   170  				// matching end/start user keys but where the left-side file
   171  				// has the sentinel key as its end key trailer. In this case
   172  				// we print the sstables as:
   173  				//
   174  				// a------d------g
   175  				//
   176  				continue
   177  			}
   178  			buf.WriteByte(middleChar)
   179  			buf.WriteByte(f.Largest.UserKey[0])
   180  			if j < len(files)-1 {
   181  				buf.WriteByte(' ')
   182  			}
   183  			lastChar++
   184  		}
   185  		fmt.Fprintf(&buf, "\n")
   186  	}
   187  	for i := len(s.levelFiles) - 1; i >= 0; i-- {
   188  		printLevel(s.levelFiles[i], fmt.Sprintf("0.%d", i), true)
   189  	}
   190  	for i := range otherLevels {
   191  		if len(otherLevels[i]) == 0 {
   192  			continue
   193  		}
   194  		printLevel(otherLevels[i], strconv.Itoa(i+1), false)
   195  	}
   196  	buf.WriteString("       ")
   197  	for b := byte('a'); b <= largestChar; b++ {
   198  		buf.WriteByte(b)
   199  		buf.WriteByte(b)
   200  		if b < largestChar {
   201  			buf.WriteByte(' ')
   202  		}
   203  	}
   204  	buf.WriteByte('\n')
   205  	return buf.String()
   206  }
   207  
   208  func TestL0Sublevels(t *testing.T) {
   209  	parseMeta := func(s string) (*FileMetadata, error) {
   210  		parts := strings.Split(s, ":")
   211  		if len(parts) != 2 {
   212  			t.Fatalf("malformed table spec: %s", s)
   213  		}
   214  		fileNum, err := strconv.Atoi(strings.TrimSpace(parts[0]))
   215  		if err != nil {
   216  			return nil, err
   217  		}
   218  		fields := strings.Fields(parts[1])
   219  		keyRange := strings.Split(strings.TrimSpace(fields[0]), "-")
   220  		m := (&FileMetadata{}).ExtendPointKeyBounds(
   221  			base.DefaultComparer.Compare,
   222  			base.ParseInternalKey(strings.TrimSpace(keyRange[0])),
   223  			base.ParseInternalKey(strings.TrimSpace(keyRange[1])),
   224  		)
   225  		m.SmallestSeqNum = m.Smallest.SeqNum()
   226  		m.LargestSeqNum = m.Largest.SeqNum()
   227  		if m.Largest.IsExclusiveSentinel() {
   228  			m.LargestSeqNum = m.SmallestSeqNum
   229  		}
   230  		m.FileNum = base.FileNum(fileNum)
   231  		m.Size = uint64(256)
   232  
   233  		if len(fields) > 1 {
   234  			for _, field := range fields[1:] {
   235  				parts := strings.Split(field, "=")
   236  				switch parts[0] {
   237  				case "base_compacting":
   238  					m.IsIntraL0Compacting = false
   239  					m.CompactionState = CompactionStateCompacting
   240  				case "intra_l0_compacting":
   241  					m.IsIntraL0Compacting = true
   242  					m.CompactionState = CompactionStateCompacting
   243  				case "compacting":
   244  					m.CompactionState = CompactionStateCompacting
   245  				case "size":
   246  					sizeInt, err := strconv.Atoi(parts[1])
   247  					if err != nil {
   248  						return nil, err
   249  					}
   250  					m.Size = uint64(sizeInt)
   251  				}
   252  			}
   253  		}
   254  
   255  		return m, nil
   256  	}
   257  
   258  	var err error
   259  	var fileMetas [NumLevels][]*FileMetadata
   260  	var explicitSublevels [][]*FileMetadata
   261  	var activeCompactions []L0Compaction
   262  	var sublevels *L0Sublevels
   263  	baseLevel := NumLevels - 1
   264  
   265  	datadriven.RunTest(t, "testdata/l0_sublevels", func(td *datadriven.TestData) string {
   266  		pickBaseCompaction := false
   267  		level := 0
   268  		addL0FilesOpt := false
   269  		switch td.Cmd {
   270  		case "add-l0-files":
   271  			addL0FilesOpt = true
   272  			level = 0
   273  			fallthrough
   274  		case "define":
   275  			if !addL0FilesOpt {
   276  				fileMetas = [NumLevels][]*FileMetadata{}
   277  				baseLevel = NumLevels - 1
   278  				activeCompactions = nil
   279  			}
   280  			explicitSublevels = [][]*FileMetadata{}
   281  			sublevel := -1
   282  			addedL0Files := make([]*FileMetadata, 0)
   283  			for _, data := range strings.Split(td.Input, "\n") {
   284  				data = strings.TrimSpace(data)
   285  				switch data[:2] {
   286  				case "L0", "L1", "L2", "L3", "L4", "L5", "L6":
   287  					level, err = strconv.Atoi(data[1:2])
   288  					if err != nil {
   289  						return err.Error()
   290  					}
   291  					if level == 0 && len(data) > 3 {
   292  						// Sublevel was specified.
   293  						sublevel, err = strconv.Atoi(data[3:])
   294  						if err != nil {
   295  							return err.Error()
   296  						}
   297  					} else {
   298  						sublevel = -1
   299  					}
   300  				default:
   301  					meta, err := parseMeta(data)
   302  					if err != nil {
   303  						return err.Error()
   304  					}
   305  					if level != 0 && level < baseLevel {
   306  						baseLevel = level
   307  					}
   308  					fileMetas[level] = append(fileMetas[level], meta)
   309  					if level == 0 {
   310  						addedL0Files = append(addedL0Files, meta)
   311  					}
   312  					if sublevel != -1 {
   313  						for len(explicitSublevels) <= sublevel {
   314  							explicitSublevels = append(explicitSublevels, []*FileMetadata{})
   315  						}
   316  						explicitSublevels[sublevel] = append(explicitSublevels[sublevel], meta)
   317  					}
   318  				}
   319  			}
   320  
   321  			flushSplitMaxBytes := 64
   322  			initialize := true
   323  			for _, arg := range td.CmdArgs {
   324  				switch arg.Key {
   325  				case "flush_split_max_bytes":
   326  					flushSplitMaxBytes, err = strconv.Atoi(arg.Vals[0])
   327  					if err != nil {
   328  						t.Fatal(err)
   329  					}
   330  				case "no_initialize":
   331  					// This case is for use with explicitly-specified sublevels
   332  					// only.
   333  					initialize = false
   334  				}
   335  			}
   336  			SortBySeqNum(fileMetas[0])
   337  			for i := 1; i < NumLevels; i++ {
   338  				SortBySmallest(fileMetas[i], base.DefaultComparer.Compare)
   339  			}
   340  
   341  			levelMetadata := makeLevelMetadata(base.DefaultComparer.Compare, 0, fileMetas[0])
   342  			if initialize {
   343  				if addL0FilesOpt {
   344  					SortBySeqNum(addedL0Files)
   345  					sublevels, err = sublevels.AddL0Files(addedL0Files, int64(flushSplitMaxBytes), &levelMetadata)
   346  					// Check if the output matches a full initialization.
   347  					sublevels2, _ := NewL0Sublevels(&levelMetadata, base.DefaultComparer.Compare, base.DefaultFormatter, int64(flushSplitMaxBytes))
   348  					if sublevels != nil && sublevels2 != nil {
   349  						require.Equal(t, sublevels.flushSplitUserKeys, sublevels2.flushSplitUserKeys)
   350  						require.Equal(t, sublevels.levelFiles, sublevels2.levelFiles)
   351  					}
   352  				} else {
   353  					sublevels, err = NewL0Sublevels(
   354  						&levelMetadata,
   355  						base.DefaultComparer.Compare,
   356  						base.DefaultFormatter,
   357  						int64(flushSplitMaxBytes))
   358  				}
   359  				if err != nil {
   360  					return err.Error()
   361  				}
   362  				sublevels.InitCompactingFileInfo(nil)
   363  			} else {
   364  				// This case is for use with explicitly-specified sublevels
   365  				// only.
   366  				sublevels = &L0Sublevels{
   367  					levelFiles:    explicitSublevels,
   368  					cmp:           base.DefaultComparer.Compare,
   369  					formatKey:     base.DefaultFormatter,
   370  					levelMetadata: &levelMetadata,
   371  				}
   372  				for _, files := range explicitSublevels {
   373  					sublevels.Levels = append(sublevels.Levels, NewLevelSliceSpecificOrder(files))
   374  				}
   375  			}
   376  
   377  			if err != nil {
   378  				t.Fatal(err)
   379  			}
   380  
   381  			var builder strings.Builder
   382  			builder.WriteString(sublevels.describe(true))
   383  			builder.WriteString(visualizeSublevels(sublevels, nil, fileMetas[1:]))
   384  			return builder.String()
   385  		case "pick-base-compaction":
   386  			pickBaseCompaction = true
   387  			fallthrough
   388  		case "pick-intra-l0-compaction":
   389  			minCompactionDepth := 3
   390  			earliestUnflushedSeqNum := uint64(math.MaxUint64)
   391  			for _, arg := range td.CmdArgs {
   392  				switch arg.Key {
   393  				case "min_depth":
   394  					minCompactionDepth, err = strconv.Atoi(arg.Vals[0])
   395  					if err != nil {
   396  						t.Fatal(err)
   397  					}
   398  				case "earliest_unflushed_seqnum":
   399  					eusnInt, err := strconv.Atoi(arg.Vals[0])
   400  					if err != nil {
   401  						t.Fatal(err)
   402  					}
   403  					earliestUnflushedSeqNum = uint64(eusnInt)
   404  				}
   405  			}
   406  
   407  			var lcf *L0CompactionFiles
   408  			if pickBaseCompaction {
   409  				baseFiles := NewLevelSliceKeySorted(base.DefaultComparer.Compare, fileMetas[baseLevel])
   410  				lcf, err = sublevels.PickBaseCompaction(minCompactionDepth, baseFiles)
   411  				if err == nil && lcf != nil {
   412  					// Try to extend the base compaction into a more rectangular
   413  					// shape, using the smallest/largest keys of the files before
   414  					// and after overlapping base files. This mimics the logic
   415  					// the compactor is expected to implement.
   416  					baseFiles := fileMetas[baseLevel]
   417  					firstFile := sort.Search(len(baseFiles), func(i int) bool {
   418  						return sublevels.cmp(baseFiles[i].Largest.UserKey, sublevels.orderedIntervals[lcf.minIntervalIndex].startKey.key) >= 0
   419  					})
   420  					lastFile := sort.Search(len(baseFiles), func(i int) bool {
   421  						return sublevels.cmp(baseFiles[i].Smallest.UserKey, sublevels.orderedIntervals[lcf.maxIntervalIndex+1].startKey.key) >= 0
   422  					})
   423  					startKey := base.InvalidInternalKey
   424  					endKey := base.InvalidInternalKey
   425  					if firstFile > 0 {
   426  						startKey = baseFiles[firstFile-1].Largest
   427  					}
   428  					if lastFile < len(baseFiles) {
   429  						endKey = baseFiles[lastFile].Smallest
   430  					}
   431  					sublevels.ExtendL0ForBaseCompactionTo(
   432  						startKey,
   433  						endKey,
   434  						lcf)
   435  				}
   436  			} else {
   437  				lcf, err = sublevels.PickIntraL0Compaction(earliestUnflushedSeqNum, minCompactionDepth)
   438  			}
   439  			if err != nil {
   440  				return fmt.Sprintf("error: %s", err.Error())
   441  			}
   442  			if lcf == nil {
   443  				return "no compaction picked"
   444  			}
   445  			var builder strings.Builder
   446  			builder.WriteString(fmt.Sprintf("compaction picked with stack depth reduction %d\n", lcf.seedIntervalStackDepthReduction))
   447  			for i, file := range lcf.Files {
   448  				builder.WriteString(file.FileNum.String())
   449  				if i < len(lcf.Files)-1 {
   450  					builder.WriteByte(',')
   451  				}
   452  			}
   453  			startKey := sublevels.orderedIntervals[lcf.seedInterval].startKey
   454  			endKey := sublevels.orderedIntervals[lcf.seedInterval+1].startKey
   455  			builder.WriteString(fmt.Sprintf("\nseed interval: %s-%s\n", startKey.key, endKey.key))
   456  			builder.WriteString(visualizeSublevels(sublevels, lcf.FilesIncluded, fileMetas[1:]))
   457  
   458  			return builder.String()
   459  		case "read-amp":
   460  			return strconv.Itoa(sublevels.ReadAmplification())
   461  		case "in-use-key-ranges":
   462  			var buf bytes.Buffer
   463  			for _, data := range strings.Split(strings.TrimSpace(td.Input), "\n") {
   464  				keyRange := strings.Split(strings.TrimSpace(data), "-")
   465  				smallest := []byte(strings.TrimSpace(keyRange[0]))
   466  				largest := []byte(strings.TrimSpace(keyRange[1]))
   467  
   468  				keyRanges := sublevels.InUseKeyRanges(smallest, largest)
   469  				for i, r := range keyRanges {
   470  					fmt.Fprintf(&buf, "%s-%s", sublevels.formatKey(r.Start), sublevels.formatKey(r.End))
   471  					if i < len(keyRanges)-1 {
   472  						fmt.Fprint(&buf, ", ")
   473  					}
   474  				}
   475  				if len(keyRanges) == 0 {
   476  					fmt.Fprint(&buf, ".")
   477  				}
   478  				fmt.Fprintln(&buf)
   479  			}
   480  			return buf.String()
   481  		case "flush-split-keys":
   482  			var builder strings.Builder
   483  			builder.WriteString("flush user split keys: ")
   484  			flushSplitKeys := sublevels.FlushSplitKeys()
   485  			for i, key := range flushSplitKeys {
   486  				builder.Write(key)
   487  				if i < len(flushSplitKeys)-1 {
   488  					builder.WriteString(", ")
   489  				}
   490  			}
   491  			if len(flushSplitKeys) == 0 {
   492  				builder.WriteString("none")
   493  			}
   494  			return builder.String()
   495  		case "max-depth-after-ongoing-compactions":
   496  			return strconv.Itoa(sublevels.MaxDepthAfterOngoingCompactions())
   497  		case "l0-check-ordering":
   498  			for sublevel, files := range sublevels.levelFiles {
   499  				slice := NewLevelSliceSpecificOrder(files)
   500  				err := CheckOrdering(base.DefaultComparer.Compare, base.DefaultFormatter,
   501  					L0Sublevel(sublevel), slice.Iter())
   502  				if err != nil {
   503  					return err.Error()
   504  				}
   505  			}
   506  			return "OK"
   507  		case "update-state-for-compaction":
   508  			var fileNums []base.FileNum
   509  			for _, arg := range td.CmdArgs {
   510  				switch arg.Key {
   511  				case "files":
   512  					for _, val := range arg.Vals {
   513  						fileNum, err := strconv.ParseUint(val, 10, 64)
   514  						if err != nil {
   515  							return err.Error()
   516  						}
   517  						fileNums = append(fileNums, base.FileNum(fileNum))
   518  					}
   519  				}
   520  			}
   521  			files := make([]*FileMetadata, 0, len(fileNums))
   522  			for _, num := range fileNums {
   523  				for _, f := range fileMetas[0] {
   524  					if f.FileNum == num {
   525  						f.CompactionState = CompactionStateCompacting
   526  						files = append(files, f)
   527  						break
   528  					}
   529  				}
   530  			}
   531  			slice := NewLevelSliceSeqSorted(files)
   532  			sm, la := KeyRange(base.DefaultComparer.Compare, slice.Iter())
   533  			activeCompactions = append(activeCompactions, L0Compaction{Smallest: sm, Largest: la})
   534  			if err := sublevels.UpdateStateForStartedCompaction([]LevelSlice{slice}, true); err != nil {
   535  				return err.Error()
   536  			}
   537  			return "OK"
   538  		case "describe":
   539  			var builder strings.Builder
   540  			builder.WriteString(sublevels.describe(true))
   541  			builder.WriteString(visualizeSublevels(sublevels, nil, fileMetas[1:]))
   542  			return builder.String()
   543  		}
   544  		return fmt.Sprintf("unrecognized command: %s", td.Cmd)
   545  	})
   546  }
   547  
   548  func TestAddL0FilesEquivalence(t *testing.T) {
   549  	seed := uint64(time.Now().UnixNano())
   550  	rng := rand.New(rand.NewSource(seed))
   551  	t.Logf("seed: %d", seed)
   552  
   553  	var inUseKeys [][]byte
   554  	const keyReusePct = 0.15
   555  	var fileMetas []*FileMetadata
   556  	var s, s2 *L0Sublevels
   557  	keySpace := testkeys.Alpha(8)
   558  
   559  	flushSplitMaxBytes := rng.Int63n(1 << 20)
   560  
   561  	// The outer loop runs once for each version edit. The inner loop(s) run
   562  	// once for each file, or each file bound.
   563  	for i := 0; i < 100; i++ {
   564  		var filesToAdd []*FileMetadata
   565  		numFiles := 1 + rng.Intn(9)
   566  		keys := make([][]byte, 0, 2*numFiles)
   567  		for j := 0; j < 2*numFiles; j++ {
   568  			if rng.Float64() <= keyReusePct && len(inUseKeys) > 0 {
   569  				keys = append(keys, inUseKeys[rng.Intn(len(inUseKeys))])
   570  			} else {
   571  				newKey := testkeys.Key(keySpace, rng.Intn(keySpace.Count()))
   572  				inUseKeys = append(inUseKeys, newKey)
   573  				keys = append(keys, newKey)
   574  			}
   575  		}
   576  		sort.Slice(keys, func(i, j int) bool {
   577  			return bytes.Compare(keys[i], keys[j]) < 0
   578  		})
   579  		for j := 0; j < numFiles; j++ {
   580  			startKey := keys[j*2]
   581  			endKey := keys[j*2+1]
   582  			if bytes.Equal(startKey, endKey) {
   583  				continue
   584  			}
   585  			meta := (&FileMetadata{
   586  				FileNum:        base.FileNum(i*10 + j + 1),
   587  				Size:           rng.Uint64n(1 << 20),
   588  				SmallestSeqNum: uint64(2*i + 1),
   589  				LargestSeqNum:  uint64(2*i + 2),
   590  			}).ExtendPointKeyBounds(
   591  				base.DefaultComparer.Compare,
   592  				base.MakeInternalKey(startKey, uint64(2*i+1), base.InternalKeyKindSet),
   593  				base.MakeRangeDeleteSentinelKey(endKey),
   594  			)
   595  			fileMetas = append(fileMetas, meta)
   596  			filesToAdd = append(filesToAdd, meta)
   597  		}
   598  		if len(filesToAdd) == 0 {
   599  			continue
   600  		}
   601  
   602  		levelMetadata := makeLevelMetadata(testkeys.Comparer.Compare, 0, fileMetas)
   603  		var err error
   604  
   605  		if s2 == nil {
   606  			s2, err = NewL0Sublevels(&levelMetadata, testkeys.Comparer.Compare, testkeys.Comparer.FormatKey, flushSplitMaxBytes)
   607  			require.NoError(t, err)
   608  		} else {
   609  			// AddL0Files relies on the indices in FileMetadatas pointing to that of
   610  			// the previous L0Sublevels. So it must be called before NewL0Sublevels;
   611  			// calling it the other way around results in out-of-bounds panics.
   612  			SortBySeqNum(filesToAdd)
   613  			s2, err = s2.AddL0Files(filesToAdd, flushSplitMaxBytes, &levelMetadata)
   614  			require.NoError(t, err)
   615  		}
   616  
   617  		s, err = NewL0Sublevels(&levelMetadata, testkeys.Comparer.Compare, testkeys.Comparer.FormatKey, flushSplitMaxBytes)
   618  		require.NoError(t, err)
   619  
   620  		// Check for equivalence.
   621  		require.Equal(t, s.flushSplitUserKeys, s2.flushSplitUserKeys)
   622  		require.Equal(t, s.orderedIntervals, s2.orderedIntervals)
   623  		require.Equal(t, s.levelFiles, s2.levelFiles)
   624  	}
   625  }
   626  
   627  func BenchmarkManifestApplyWithL0Sublevels(b *testing.B) {
   628  	b.ResetTimer()
   629  	for n := 0; n < b.N; n++ {
   630  		v, err := readManifest("testdata/MANIFEST_import")
   631  		require.NotNil(b, v)
   632  		require.NoError(b, err)
   633  	}
   634  }
   635  
   636  func BenchmarkL0SublevelsInit(b *testing.B) {
   637  	v, err := readManifest("testdata/MANIFEST_import")
   638  	if err != nil {
   639  		b.Fatal(err)
   640  	}
   641  	b.ResetTimer()
   642  	for n := 0; n < b.N; n++ {
   643  		sl, err := NewL0Sublevels(&v.Levels[0],
   644  			base.DefaultComparer.Compare, base.DefaultFormatter, 5<<20)
   645  		require.NoError(b, err)
   646  		if sl == nil {
   647  			b.Fatal("expected non-nil L0Sublevels to be generated")
   648  		}
   649  	}
   650  }
   651  
   652  func BenchmarkL0SublevelsInitAndPick(b *testing.B) {
   653  	v, err := readManifest("testdata/MANIFEST_import")
   654  	if err != nil {
   655  		b.Fatal(err)
   656  	}
   657  	b.ResetTimer()
   658  	for n := 0; n < b.N; n++ {
   659  		sl, err := NewL0Sublevels(&v.Levels[0],
   660  			base.DefaultComparer.Compare, base.DefaultFormatter, 5<<20)
   661  		require.NoError(b, err)
   662  		if sl == nil {
   663  			b.Fatal("expected non-nil L0Sublevels to be generated")
   664  		}
   665  		c, err := sl.PickBaseCompaction(2, LevelSlice{})
   666  		require.NoError(b, err)
   667  		if c == nil {
   668  			b.Fatal("expected non-nil compaction to be generated")
   669  		}
   670  	}
   671  }