github.com/cockroachdb/pebble@v1.1.1-0.20240513155919-3622ade60459/internal/keyspan/level_iter_test.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  	"strings"
    10  	"testing"
    11  
    12  	"github.com/cockroachdb/datadriven"
    13  	"github.com/cockroachdb/pebble/internal/base"
    14  	"github.com/cockroachdb/pebble/internal/manifest"
    15  	"github.com/stretchr/testify/require"
    16  )
    17  
    18  func TestLevelIterEquivalence(t *testing.T) {
    19  	type level [][]Span
    20  	testCases := []struct {
    21  		name   string
    22  		levels []level
    23  	}{
    24  		{
    25  			"single level, no gaps, no overlaps",
    26  			[]level{
    27  				{
    28  					{
    29  						Span{
    30  							Start: []byte("a"),
    31  							End:   []byte("b"),
    32  							Keys: []Key{{
    33  								Trailer: base.MakeTrailer(2, base.InternalKeyKindRangeKeySet),
    34  								Suffix:  nil,
    35  								Value:   []byte("foo"),
    36  							}},
    37  						},
    38  						Span{
    39  							Start: []byte("b"),
    40  							End:   []byte("c"),
    41  							Keys: []Key{{
    42  								Trailer: base.MakeTrailer(2, base.InternalKeyKindRangeKeySet),
    43  								Suffix:  nil,
    44  								Value:   []byte("foo"),
    45  							}},
    46  						},
    47  						Span{
    48  							Start: []byte("c"),
    49  							End:   []byte("d"),
    50  							Keys: []Key{{
    51  								Trailer: base.MakeTrailer(2, base.InternalKeyKindRangeKeySet),
    52  								Suffix:  nil,
    53  								Value:   []byte("foo"),
    54  							}},
    55  						},
    56  					},
    57  					{
    58  						Span{
    59  							Start: []byte("d"),
    60  							End:   []byte("e"),
    61  							Keys: []Key{{
    62  								Trailer: base.MakeTrailer(2, base.InternalKeyKindRangeKeySet),
    63  								Suffix:  nil,
    64  								Value:   []byte("foo"),
    65  							}},
    66  						},
    67  						Span{
    68  							Start: []byte("e"),
    69  							End:   []byte("f"),
    70  							Keys: []Key{{
    71  								Trailer: base.MakeTrailer(2, base.InternalKeyKindRangeKeySet),
    72  								Suffix:  nil,
    73  								Value:   []byte("foo"),
    74  							}},
    75  						},
    76  						Span{
    77  							Start: []byte("f"),
    78  							End:   []byte("g"),
    79  							Keys: []Key{{
    80  								Trailer: base.MakeTrailer(2, base.InternalKeyKindRangeKeySet),
    81  								Suffix:  nil,
    82  								Value:   []byte("foo"),
    83  							}},
    84  						},
    85  					},
    86  				},
    87  			},
    88  		},
    89  		{
    90  			"single level, overlapping fragments",
    91  			[]level{
    92  				{
    93  					{
    94  						Span{
    95  							Start: []byte("a"),
    96  							End:   []byte("b"),
    97  							Keys: []Key{
    98  								{
    99  									Trailer: base.MakeTrailer(4, base.InternalKeyKindRangeKeySet),
   100  									Suffix:  nil,
   101  									Value:   []byte("bar"),
   102  								},
   103  								{
   104  									Trailer: base.MakeTrailer(2, base.InternalKeyKindRangeKeySet),
   105  									Suffix:  nil,
   106  									Value:   []byte("foo"),
   107  								},
   108  							},
   109  						},
   110  						Span{
   111  							Start: []byte("b"),
   112  							End:   []byte("c"),
   113  							Keys: []Key{
   114  								{
   115  									Trailer: base.MakeTrailer(4, base.InternalKeyKindRangeKeySet),
   116  									Suffix:  nil,
   117  									Value:   []byte("bar"),
   118  								},
   119  								{
   120  									Trailer: base.MakeTrailer(2, base.InternalKeyKindRangeKeySet),
   121  									Suffix:  nil,
   122  									Value:   []byte("foo"),
   123  								},
   124  							},
   125  						},
   126  						Span{
   127  							Start: []byte("c"),
   128  							End:   []byte("d"),
   129  							Keys: []Key{{
   130  								Trailer: base.MakeTrailer(2, base.InternalKeyKindRangeKeySet),
   131  								Suffix:  nil,
   132  								Value:   []byte("foo"),
   133  							}},
   134  						},
   135  					},
   136  					{
   137  						Span{
   138  							Start: []byte("d"),
   139  							End:   []byte("e"),
   140  							Keys: []Key{{
   141  								Trailer: base.MakeTrailer(2, base.InternalKeyKindRangeKeySet),
   142  								Suffix:  nil,
   143  								Value:   []byte("foo"),
   144  							}},
   145  						},
   146  						Span{
   147  							Start: []byte("e"),
   148  							End:   []byte("f"),
   149  							Keys: []Key{{
   150  								Trailer: base.MakeTrailer(2, base.InternalKeyKindRangeKeySet),
   151  								Suffix:  nil,
   152  								Value:   []byte("foo"),
   153  							}},
   154  						},
   155  						Span{
   156  							Start: []byte("f"),
   157  							End:   []byte("g"),
   158  							Keys: []Key{{
   159  								Trailer: base.MakeTrailer(2, base.InternalKeyKindRangeKeySet),
   160  								Suffix:  nil,
   161  								Value:   []byte("foo"),
   162  							}},
   163  						},
   164  					},
   165  				},
   166  			},
   167  		},
   168  		{
   169  			"single level, gaps between files and range keys",
   170  			[]level{
   171  				{
   172  					{
   173  						Span{
   174  							Start: []byte("a"),
   175  							End:   []byte("b"),
   176  							Keys: []Key{{
   177  								Trailer: base.MakeTrailer(2, base.InternalKeyKindRangeKeySet),
   178  								Suffix:  nil,
   179  								Value:   []byte("foo"),
   180  							}},
   181  						},
   182  						Span{
   183  							Start: []byte("c"),
   184  							End:   []byte("d"),
   185  							Keys: []Key{{
   186  								Trailer: base.MakeTrailer(2, base.InternalKeyKindRangeKeySet),
   187  								Suffix:  nil,
   188  								Value:   []byte("foo"),
   189  							}},
   190  						},
   191  						Span{
   192  							Start: []byte("e"),
   193  							End:   []byte("f"),
   194  							Keys: []Key{{
   195  								Trailer: base.MakeTrailer(2, base.InternalKeyKindRangeKeySet),
   196  								Suffix:  nil,
   197  								Value:   []byte("foo"),
   198  							}},
   199  						},
   200  					},
   201  					{
   202  						Span{
   203  							Start: []byte("g"),
   204  							End:   []byte("h"),
   205  							Keys: []Key{{
   206  								Trailer: base.MakeTrailer(2, base.InternalKeyKindRangeKeySet),
   207  								Suffix:  nil,
   208  								Value:   []byte("foo"),
   209  							}},
   210  						},
   211  						Span{
   212  							Start: []byte("i"),
   213  							End:   []byte("j"),
   214  							Keys: []Key{{
   215  								Trailer: base.MakeTrailer(2, base.InternalKeyKindRangeKeySet),
   216  								Suffix:  nil,
   217  								Value:   []byte("foo"),
   218  							}},
   219  						},
   220  						Span{
   221  							Start: []byte("k"),
   222  							End:   []byte("l"),
   223  							Keys: []Key{{
   224  								Trailer: base.MakeTrailer(2, base.InternalKeyKindRangeKeySet),
   225  								Suffix:  nil,
   226  								Value:   []byte("foo"),
   227  							}},
   228  						},
   229  					},
   230  				},
   231  			},
   232  		},
   233  		{
   234  			"two levels, one with overlapping unset",
   235  			[]level{
   236  				{
   237  					{
   238  						Span{
   239  							Start: []byte("a"),
   240  							End:   []byte("h"),
   241  							Keys: []Key{{
   242  								Trailer: base.MakeTrailer(2, base.InternalKeyKindRangeKeySet),
   243  								Suffix:  nil,
   244  								Value:   []byte("foo"),
   245  							}},
   246  						},
   247  					},
   248  					{
   249  						Span{
   250  							Start: []byte("l"),
   251  							End:   []byte("u"),
   252  							Keys: []Key{{
   253  								Trailer: base.MakeTrailer(2, base.InternalKeyKindRangeKeyUnset),
   254  								Suffix:  nil,
   255  								Value:   nil,
   256  							}},
   257  						},
   258  					},
   259  				},
   260  				{
   261  					{
   262  						Span{
   263  							Start: []byte("e"),
   264  							End:   []byte("r"),
   265  							Keys: []Key{{
   266  								Trailer: base.MakeTrailer(1, base.InternalKeyKindRangeKeySet),
   267  								Suffix:  nil,
   268  								Value:   []byte("foo"),
   269  							}},
   270  						},
   271  					},
   272  				},
   273  			},
   274  		},
   275  	}
   276  
   277  	for _, tc := range testCases {
   278  		var fileIters []FragmentIterator
   279  		var levelIters []FragmentIterator
   280  		var iter1, iter2 MergingIter
   281  		for j, level := range tc.levels {
   282  			j := j // Copy for use in closures down below.
   283  			var levelIter LevelIter
   284  			var metas []*manifest.FileMetadata
   285  			for k, file := range level {
   286  				fileIters = append(fileIters, NewIter(base.DefaultComparer.Compare, file))
   287  				meta := &manifest.FileMetadata{
   288  					FileNum:          base.FileNum(k + 1),
   289  					Size:             1024,
   290  					SmallestSeqNum:   2,
   291  					LargestSeqNum:    2,
   292  					SmallestRangeKey: base.MakeInternalKey(file[0].Start, file[0].SmallestKey().SeqNum(), file[0].SmallestKey().Kind()),
   293  					LargestRangeKey:  base.MakeExclusiveSentinelKey(file[len(file)-1].LargestKey().Kind(), file[len(file)-1].End),
   294  					HasPointKeys:     false,
   295  					HasRangeKeys:     true,
   296  				}
   297  				meta.InitPhysicalBacking()
   298  				meta.ExtendRangeKeyBounds(base.DefaultComparer.Compare, meta.SmallestRangeKey, meta.LargestRangeKey)
   299  				metas = append(metas, meta)
   300  			}
   301  
   302  			tableNewIters := func(file *manifest.FileMetadata, iterOptions SpanIterOptions) (FragmentIterator, error) {
   303  				return NewIter(base.DefaultComparer.Compare, tc.levels[j][file.FileNum-1]), nil
   304  			}
   305  			// Add all the fileMetadatas to L6.
   306  			b := &manifest.BulkVersionEdit{}
   307  			amap := make(map[base.FileNum]*manifest.FileMetadata)
   308  			for i := range metas {
   309  				amap[metas[i].FileNum] = metas[i]
   310  			}
   311  			b.Added[6] = amap
   312  			v, err := b.Apply(nil, base.DefaultComparer.Compare, base.DefaultFormatter, 0, 0, nil, manifest.ProhibitSplitUserKeys)
   313  			require.NoError(t, err)
   314  			levelIter.Init(
   315  				SpanIterOptions{}, base.DefaultComparer.Compare, tableNewIters,
   316  				v.Levels[6].Iter(), 0, manifest.KeyTypeRange,
   317  			)
   318  			levelIters = append(levelIters, &levelIter)
   319  		}
   320  
   321  		iter1.Init(base.DefaultComparer.Compare, VisibleTransform(base.InternalKeySeqNumMax), new(MergingBuffers), fileIters...)
   322  		iter2.Init(base.DefaultComparer.Compare, VisibleTransform(base.InternalKeySeqNumMax), new(MergingBuffers), levelIters...)
   323  		// Check iter1 and iter2 for equivalence.
   324  
   325  		require.Equal(t, iter1.First(), iter2.First(), "failed on test case %q", tc.name)
   326  		valid := true
   327  		for valid {
   328  			f1 := iter1.Next()
   329  			var f2 *Span
   330  			for {
   331  				f2 = iter2.Next()
   332  				// The level iter could produce empty spans that straddle between
   333  				// files. Ignore those.
   334  				if f2 == nil || !f2.Empty() {
   335  					break
   336  				}
   337  			}
   338  
   339  			require.Equal(t, f1, f2, "failed on test case %q", tc.name)
   340  			valid = f1 != nil && f2 != nil
   341  		}
   342  	}
   343  }
   344  
   345  func TestLevelIter(t *testing.T) {
   346  	var level [][]Span
   347  	var rangedels [][]Span
   348  	var metas []*manifest.FileMetadata
   349  	var iter FragmentIterator
   350  	var extraInfo func() string
   351  
   352  	datadriven.RunTest(t, "testdata/level_iter", func(t *testing.T, d *datadriven.TestData) string {
   353  		switch d.Cmd {
   354  		case "define":
   355  			level = level[:0]
   356  			metas = metas[:0]
   357  			rangedels = rangedels[:0]
   358  			if iter != nil {
   359  				iter.Close()
   360  				iter = nil
   361  			}
   362  			var pointKeys []base.InternalKey
   363  			var currentRangeDels []Span
   364  			var currentFile []Span
   365  			for _, key := range strings.Split(d.Input, "\n") {
   366  				if strings.HasPrefix(key, "file") {
   367  					// Skip the very first file creation.
   368  					if len(level) != 0 || len(currentFile) != 0 {
   369  						meta := &manifest.FileMetadata{
   370  							FileNum: base.FileNum(len(level) + 1),
   371  						}
   372  						if len(currentFile) > 0 {
   373  							smallest := base.MakeInternalKey(currentFile[0].Start, currentFile[0].SmallestKey().SeqNum(), currentFile[0].SmallestKey().Kind())
   374  							largest := base.MakeExclusiveSentinelKey(currentFile[len(currentFile)-1].LargestKey().Kind(), currentFile[len(currentFile)-1].End)
   375  							meta.ExtendRangeKeyBounds(base.DefaultComparer.Compare, smallest, largest)
   376  						}
   377  						if len(pointKeys) != 0 {
   378  							meta.ExtendPointKeyBounds(base.DefaultComparer.Compare, pointKeys[0], pointKeys[len(pointKeys)-1])
   379  						}
   380  						meta.InitPhysicalBacking()
   381  						level = append(level, currentFile)
   382  						metas = append(metas, meta)
   383  						rangedels = append(rangedels, currentRangeDels)
   384  						currentRangeDels = nil
   385  						currentFile = nil
   386  						pointKeys = nil
   387  					}
   388  					continue
   389  				}
   390  				key = strings.TrimSpace(key)
   391  				if strings.HasPrefix(key, "point:") {
   392  					key = strings.TrimPrefix(key, "point:")
   393  					j := strings.Index(key, ":")
   394  					ikey := base.ParseInternalKey(key[:j])
   395  					pointKeys = append(pointKeys, ikey)
   396  					if ikey.Kind() == base.InternalKeyKindRangeDelete {
   397  						currentRangeDels = append(currentRangeDels, Span{
   398  							Start: ikey.UserKey, End: []byte(key[j+1:]), Keys: []Key{{Trailer: ikey.Trailer}}})
   399  					}
   400  					continue
   401  				}
   402  				span := ParseSpan(key)
   403  				currentFile = append(currentFile, span)
   404  			}
   405  			meta := &manifest.FileMetadata{
   406  				FileNum: base.FileNum(len(level) + 1),
   407  			}
   408  			meta.InitPhysicalBacking()
   409  			level = append(level, currentFile)
   410  			rangedels = append(rangedels, currentRangeDels)
   411  			if len(currentFile) > 0 {
   412  				smallest := base.MakeInternalKey(currentFile[0].Start, currentFile[0].SmallestKey().SeqNum(), currentFile[0].SmallestKey().Kind())
   413  				largest := base.MakeExclusiveSentinelKey(currentFile[len(currentFile)-1].LargestKey().Kind(), currentFile[len(currentFile)-1].End)
   414  				meta.ExtendRangeKeyBounds(base.DefaultComparer.Compare, smallest, largest)
   415  			}
   416  			if len(pointKeys) != 0 {
   417  				meta.ExtendPointKeyBounds(base.DefaultComparer.Compare, pointKeys[0], pointKeys[len(pointKeys)-1])
   418  			}
   419  			metas = append(metas, meta)
   420  			return ""
   421  		case "num-files":
   422  			return fmt.Sprintf("%d", len(level))
   423  		case "close-iter":
   424  			_ = iter.Close()
   425  			iter = nil
   426  			return "ok"
   427  		case "iter":
   428  			keyType := manifest.KeyTypeRange
   429  			for _, arg := range d.CmdArgs {
   430  				if strings.Contains(arg.Key, "rangedel") {
   431  					keyType = manifest.KeyTypePoint
   432  				}
   433  			}
   434  			if iter == nil {
   435  				var lastFileNum base.FileNum
   436  				tableNewIters := func(file *manifest.FileMetadata, _ SpanIterOptions) (FragmentIterator, error) {
   437  					keyType := keyType
   438  					spans := level[file.FileNum-1]
   439  					if keyType == manifest.KeyTypePoint {
   440  						spans = rangedels[file.FileNum-1]
   441  					}
   442  					lastFileNum = file.FileNum
   443  					return NewIter(base.DefaultComparer.Compare, spans), nil
   444  				}
   445  				b := &manifest.BulkVersionEdit{}
   446  				amap := make(map[base.FileNum]*manifest.FileMetadata)
   447  				for i := range metas {
   448  					amap[metas[i].FileNum] = metas[i]
   449  				}
   450  				b.Added[6] = amap
   451  				v, err := b.Apply(nil, base.DefaultComparer.Compare, base.DefaultFormatter, 0, 0, nil, manifest.ProhibitSplitUserKeys)
   452  				require.NoError(t, err)
   453  				iter = NewLevelIter(
   454  					SpanIterOptions{}, base.DefaultComparer.Compare,
   455  					tableNewIters, v.Levels[6].Iter(), 6, keyType,
   456  				)
   457  				extraInfo = func() string {
   458  					return fmt.Sprintf("file = %s.sst", lastFileNum)
   459  				}
   460  			}
   461  
   462  			return runFragmentIteratorCmd(iter, d.Input, extraInfo)
   463  
   464  		default:
   465  			return fmt.Sprintf("unknown command: %s", d.Cmd)
   466  		}
   467  	})
   468  
   469  	if iter != nil {
   470  		iter.Close()
   471  	}
   472  }