github.com/cockroachdb/pebble@v1.1.1-0.20240513155919-3622ade60459/get_iter_test.go (about)

     1  // Copyright 2018 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 pebble
     6  
     7  import (
     8  	"context"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/cockroachdb/errors"
    13  	"github.com/cockroachdb/pebble/internal/base"
    14  	"github.com/cockroachdb/pebble/internal/keyspan"
    15  	"github.com/cockroachdb/pebble/internal/manifest"
    16  	"github.com/cockroachdb/pebble/internal/testkeys"
    17  )
    18  
    19  func TestGetIter(t *testing.T) {
    20  	// testTable is a table to insert into a version.
    21  	// Each element of data is a string of the form "internalKey value".
    22  	type testTable struct {
    23  		level   int
    24  		fileNum FileNum
    25  		data    []string
    26  	}
    27  
    28  	testCases := []struct {
    29  		description string
    30  		// badOrdering is whether this test case has a table ordering violation.
    31  		badOrdering bool
    32  		// tables are the tables to populate the version with.
    33  		tables []testTable
    34  		// queries are the queries to run against the version. Each element has
    35  		// the form "internalKey wantedValue". The internalKey is passed to the
    36  		// version.get method, wantedValue may be "ErrNotFound" if the query
    37  		// should return that error.
    38  		queries []string
    39  	}{
    40  		{
    41  			description: "empty: an empty version",
    42  			queries: []string{
    43  				"abc.SEPARATOR.101 ErrNotFound",
    44  			},
    45  		},
    46  
    47  		{
    48  			description: "single-0: one level-0 table",
    49  			tables: []testTable{
    50  				{
    51  					level:   0,
    52  					fileNum: 10,
    53  					data: []string{
    54  						"the.SET.101 a",
    55  						"cat.SET.102 b",
    56  						"on_.SET.103 c",
    57  						"the.SET.104 d",
    58  						"mat.SET.105 e",
    59  						"the.DEL.106 ",
    60  						"the.MERGE.107 g",
    61  					},
    62  				},
    63  			},
    64  			queries: []string{
    65  				"aaa.SEPARATOR.105 ErrNotFound",
    66  				"cat.SEPARATOR.105 b",
    67  				"hat.SEPARATOR.105 ErrNotFound",
    68  				"mat.SEPARATOR.105 e",
    69  				"the.SEPARATOR.108 g",
    70  				"the.SEPARATOR.107 g",
    71  				"the.SEPARATOR.106 ErrNotFound",
    72  				"the.SEPARATOR.105 d",
    73  				"the.SEPARATOR.104 d",
    74  				"the.SEPARATOR.104 d",
    75  				"the.SEPARATOR.103 a",
    76  				"the.SEPARATOR.102 a",
    77  				"the.SEPARATOR.101 a",
    78  				"the.SEPARATOR.100 ErrNotFound",
    79  				"zzz.SEPARATOR.105 ErrNotFound",
    80  			},
    81  		},
    82  
    83  		{
    84  			description: "triple-0: three level-0 tables",
    85  			tables: []testTable{
    86  				{
    87  					level:   0,
    88  					fileNum: 10,
    89  					data: []string{
    90  						"the.SET.101 a",
    91  						"cat.SET.102 b",
    92  						"on_.SET.103 c",
    93  						"the.SET.104 d",
    94  						"mat.SET.105 e",
    95  						"the.DEL.106 ",
    96  						"the.SET.107 g",
    97  					},
    98  				},
    99  				{
   100  					level:   0,
   101  					fileNum: 11,
   102  					data: []string{
   103  						"awk.SET.111 w",
   104  						"cat.SET.112 x",
   105  						"man.SET.113 y",
   106  						"sed.SET.114 z",
   107  					},
   108  				},
   109  				{
   110  					level:   0,
   111  					fileNum: 12,
   112  					data: []string{
   113  						"the.DEL.121 ",
   114  						"cat.DEL.122 ",
   115  						"man.DEL.123 ",
   116  						"was.SET.124 D",
   117  						"not.SET.125 E",
   118  						"the.SET.126 F",
   119  						"man.SET.127 G",
   120  					},
   121  				},
   122  			},
   123  			queries: []string{
   124  				"aaa.SEPARATOR.105 ErrNotFound",
   125  				"awk.SEPARATOR.135 w",
   126  				"awk.SEPARATOR.125 w",
   127  				"awk.SEPARATOR.115 w",
   128  				"awk.SEPARATOR.105 ErrNotFound",
   129  				"cat.SEPARATOR.135 ErrNotFound",
   130  				"cat.SEPARATOR.125 ErrNotFound",
   131  				"cat.SEPARATOR.115 x",
   132  				"cat.SEPARATOR.105 b",
   133  				"man.SEPARATOR.135 G",
   134  				"man.SEPARATOR.125 ErrNotFound",
   135  				"man.SEPARATOR.115 y",
   136  				"man.SEPARATOR.105 ErrNotFound",
   137  				"on_.SEPARATOR.135 c",
   138  				"on_.SEPARATOR.125 c",
   139  				"on_.SEPARATOR.115 c",
   140  				"on_.SEPARATOR.105 c",
   141  				"the.SEPARATOR.135 F",
   142  				"the.SEPARATOR.127 F",
   143  				"the.SEPARATOR.126 F",
   144  				"the.SEPARATOR.125 ErrNotFound",
   145  				"the.SEPARATOR.122 ErrNotFound",
   146  				"the.SEPARATOR.121 ErrNotFound",
   147  				"the.SEPARATOR.120 g",
   148  				"the.SEPARATOR.115 g",
   149  				"the.SEPARATOR.114 g",
   150  				"the.SEPARATOR.111 g",
   151  				"the.SEPARATOR.110 g",
   152  				"the.SEPARATOR.108 g",
   153  				"the.SEPARATOR.107 g",
   154  				"the.SEPARATOR.106 ErrNotFound",
   155  				"the.SEPARATOR.105 d",
   156  				"the.SEPARATOR.104 d",
   157  				"the.SEPARATOR.104 d",
   158  				"the.SEPARATOR.103 a",
   159  				"the.SEPARATOR.102 a",
   160  				"the.SEPARATOR.101 a",
   161  				"the.SEPARATOR.100 ErrNotFound",
   162  				"zzz.SEPARATOR.105 ErrNotFound",
   163  			},
   164  		},
   165  
   166  		{
   167  			description: "quad-4: four level-4 tables",
   168  			tables: []testTable{
   169  				{
   170  					level:   4,
   171  					fileNum: 11,
   172  					data: []string{
   173  						"aardvark.SET.101 a1",
   174  						"alpaca__.SET.201 a2",
   175  						"anteater.SET.301 a3",
   176  					},
   177  				},
   178  				{
   179  					level:   4,
   180  					fileNum: 22,
   181  					data: []string{
   182  						"baboon__.SET.102 b1",
   183  						"baboon__.DEL.202 ",
   184  						"baboon__.SET.302 b3",
   185  						"bear____.SET.402 b4",
   186  						"bear____.DEL.502 ",
   187  						"buffalo_.SET.602 b6",
   188  					},
   189  				},
   190  				{
   191  					level:   4,
   192  					fileNum: 33,
   193  					data: []string{
   194  						"buffalo_.SET.103 B1",
   195  					},
   196  				},
   197  				{
   198  					level:   4,
   199  					fileNum: 44,
   200  					data: []string{
   201  						"chipmunk.SET.104 c1",
   202  						"chipmunk.SET.204 c2",
   203  					},
   204  				},
   205  			},
   206  			queries: []string{
   207  				"a_______.SEPARATOR.999 ErrNotFound",
   208  				"aardvark.SEPARATOR.999 a1",
   209  				"aardvark.SEPARATOR.102 a1",
   210  				"aardvark.SEPARATOR.101 a1",
   211  				"aardvark.SEPARATOR.100 ErrNotFound",
   212  				"alpaca__.SEPARATOR.999 a2",
   213  				"alpaca__.SEPARATOR.200 ErrNotFound",
   214  				"anteater.SEPARATOR.999 a3",
   215  				"anteater.SEPARATOR.302 a3",
   216  				"anteater.SEPARATOR.301 a3",
   217  				"anteater.SEPARATOR.300 ErrNotFound",
   218  				"anteater.SEPARATOR.000 ErrNotFound",
   219  				"b_______.SEPARATOR.999 ErrNotFound",
   220  				"baboon__.SEPARATOR.999 b3",
   221  				"baboon__.SEPARATOR.302 b3",
   222  				"baboon__.SEPARATOR.301 ErrNotFound",
   223  				"baboon__.SEPARATOR.202 ErrNotFound",
   224  				"baboon__.SEPARATOR.201 b1",
   225  				"baboon__.SEPARATOR.102 b1",
   226  				"baboon__.SEPARATOR.101 ErrNotFound",
   227  				"bear____.SEPARATOR.999 ErrNotFound",
   228  				"bear____.SEPARATOR.500 b4",
   229  				"bear____.SEPARATOR.000 ErrNotFound",
   230  				"buffalo_.SEPARATOR.999 b6",
   231  				"buffalo_.SEPARATOR.603 b6",
   232  				"buffalo_.SEPARATOR.602 b6",
   233  				"buffalo_.SEPARATOR.601 B1",
   234  				"buffalo_.SEPARATOR.104 B1",
   235  				"buffalo_.SEPARATOR.103 B1",
   236  				"buffalo_.SEPARATOR.102 ErrNotFound",
   237  				"buffalo_.SEPARATOR.000 ErrNotFound",
   238  				"c_______.SEPARATOR.999 ErrNotFound",
   239  				"chipmunk.SEPARATOR.999 c2",
   240  				"chipmunk.SEPARATOR.205 c2",
   241  				"chipmunk.SEPARATOR.204 c2",
   242  				"chipmunk.SEPARATOR.203 c1",
   243  				"chipmunk.SEPARATOR.105 c1",
   244  				"chipmunk.SEPARATOR.104 c1",
   245  				"chipmunk.SEPARATOR.103 ErrNotFound",
   246  				"chipmunk.SEPARATOR.000 ErrNotFound",
   247  				"d_______.SEPARATOR.999 ErrNotFound",
   248  			},
   249  		},
   250  
   251  		{
   252  			description: "complex: many tables at many levels",
   253  			tables: []testTable{
   254  				{
   255  					level:   0,
   256  					fileNum: 50,
   257  					data: []string{
   258  						"alfalfa__.SET.501 p1",
   259  						"asparagus.SET.502 p2",
   260  						"cabbage__.DEL.503 ",
   261  						"spinach__.MERGE.504 p3",
   262  					},
   263  				},
   264  				{
   265  					level:   0,
   266  					fileNum: 51,
   267  					data: []string{
   268  						"asparagus.SET.511 q1",
   269  						"asparagus.SET.512 q2",
   270  						"asparagus.SET.513 q3",
   271  						"beans____.SET.514 q4",
   272  						"broccoli_.SET.515 q5",
   273  						"cabbage__.SET.516 q6",
   274  						"celery___.SET.517 q7",
   275  						"spinach__.MERGE.518 q8",
   276  					},
   277  				},
   278  				{
   279  					level:   1,
   280  					fileNum: 40,
   281  					data: []string{
   282  						"alfalfa__.SET.410 r1",
   283  						"asparagus.SET.420 r2",
   284  						"arugula__.SET.430 r3",
   285  					},
   286  				},
   287  				{
   288  					level:   1,
   289  					fileNum: 41,
   290  					data: []string{
   291  						"beans____.SET.411 s1",
   292  						"beans____.SET.421 s2",
   293  						"bokchoy__.DEL.431 ",
   294  						"broccoli_.SET.441 s4",
   295  					},
   296  				},
   297  				{
   298  					level:   1,
   299  					fileNum: 42,
   300  					data: []string{
   301  						"cabbage__.SET.412 t1",
   302  						"corn_____.DEL.422 ",
   303  						"spinach__.MERGE.432 t2",
   304  					},
   305  				},
   306  				{
   307  					level:   2,
   308  					fileNum: 30,
   309  					data: []string{
   310  						"alfalfa__.SET.310 u1",
   311  						"bokchoy__.SET.320 u2",
   312  						"celery___.SET.330 u3",
   313  						"corn_____.SET.340 u4",
   314  						"spinach__.MERGE.350 u5",
   315  					},
   316  				},
   317  			},
   318  			queries: []string{
   319  				"a________.SEPARATOR.999 ErrNotFound",
   320  				"alfalfa__.SEPARATOR.520 p1",
   321  				"alfalfa__.SEPARATOR.510 p1",
   322  				"alfalfa__.SEPARATOR.500 r1",
   323  				"alfalfa__.SEPARATOR.400 u1",
   324  				"alfalfa__.SEPARATOR.300 ErrNotFound",
   325  				"asparagus.SEPARATOR.520 q3",
   326  				"asparagus.SEPARATOR.510 p2",
   327  				"asparagus.SEPARATOR.500 r2",
   328  				"asparagus.SEPARATOR.400 ErrNotFound",
   329  				"asparagus.SEPARATOR.300 ErrNotFound",
   330  				"arugula__.SEPARATOR.520 r3",
   331  				"arugula__.SEPARATOR.510 r3",
   332  				"arugula__.SEPARATOR.500 r3",
   333  				"arugula__.SEPARATOR.400 ErrNotFound",
   334  				"arugula__.SEPARATOR.300 ErrNotFound",
   335  				"beans____.SEPARATOR.520 q4",
   336  				"beans____.SEPARATOR.510 s2",
   337  				"beans____.SEPARATOR.500 s2",
   338  				"beans____.SEPARATOR.400 ErrNotFound",
   339  				"beans____.SEPARATOR.300 ErrNotFound",
   340  				"bokchoy__.SEPARATOR.520 ErrNotFound",
   341  				"bokchoy__.SEPARATOR.510 ErrNotFound",
   342  				"bokchoy__.SEPARATOR.500 ErrNotFound",
   343  				"bokchoy__.SEPARATOR.400 u2",
   344  				"bokchoy__.SEPARATOR.300 ErrNotFound",
   345  				"broccoli_.SEPARATOR.520 q5",
   346  				"broccoli_.SEPARATOR.510 s4",
   347  				"broccoli_.SEPARATOR.500 s4",
   348  				"broccoli_.SEPARATOR.400 ErrNotFound",
   349  				"broccoli_.SEPARATOR.300 ErrNotFound",
   350  				"cabbage__.SEPARATOR.520 q6",
   351  				"cabbage__.SEPARATOR.510 ErrNotFound",
   352  				"cabbage__.SEPARATOR.500 t1",
   353  				"cabbage__.SEPARATOR.400 ErrNotFound",
   354  				"cabbage__.SEPARATOR.300 ErrNotFound",
   355  				"celery___.SEPARATOR.520 q7",
   356  				"celery___.SEPARATOR.510 u3",
   357  				"celery___.SEPARATOR.500 u3",
   358  				"celery___.SEPARATOR.400 u3",
   359  				"celery___.SEPARATOR.300 ErrNotFound",
   360  				"corn_____.SEPARATOR.520 ErrNotFound",
   361  				"corn_____.SEPARATOR.510 ErrNotFound",
   362  				"corn_____.SEPARATOR.500 ErrNotFound",
   363  				"corn_____.SEPARATOR.400 u4",
   364  				"corn_____.SEPARATOR.300 ErrNotFound",
   365  				"d________.SEPARATOR.999 ErrNotFound",
   366  				"spinach__.SEPARATOR.999 u5t2p3q8",
   367  				"spinach__.SEPARATOR.518 u5t2p3q8",
   368  				"spinach__.SEPARATOR.517 u5t2p3",
   369  				"spinach__.SEPARATOR.504 u5t2p3",
   370  				"spinach__.SEPARATOR.503 u5t2",
   371  				"spinach__.SEPARATOR.432 u5t2",
   372  				"spinach__.SEPARATOR.431 u5",
   373  				"spinach__.SEPARATOR.350 u5",
   374  				"spinach__.SEPARATOR.349 ErrNotFound",
   375  			},
   376  		},
   377  
   378  		{
   379  			description: "broken invariants 0: non-increasing level 0 sequence numbers",
   380  			badOrdering: true,
   381  			tables: []testTable{
   382  				{
   383  					level:   0,
   384  					fileNum: 19,
   385  					data: []string{
   386  						"a.SET.101 a",
   387  						"b.SET.102 b",
   388  					},
   389  				},
   390  				{
   391  					level:   0,
   392  					fileNum: 20,
   393  					data: []string{
   394  						"c.SET.101 c",
   395  					},
   396  				},
   397  			},
   398  		},
   399  
   400  		{
   401  			description: "broken invariants 1: non-increasing level 0 sequence numbers",
   402  			badOrdering: true,
   403  			tables: []testTable{
   404  				{
   405  					level:   0,
   406  					fileNum: 19,
   407  					data: []string{
   408  						"a.SET.101 a",
   409  						"b.SET.102 b",
   410  					},
   411  				},
   412  				{
   413  					level:   0,
   414  					fileNum: 20,
   415  					data: []string{
   416  						"c.SET.100 c",
   417  						"d.SET.101 d",
   418  					},
   419  				},
   420  			},
   421  		},
   422  
   423  		{
   424  			description: "broken invariants 2: matching level 0 sequence numbers, considered acceptable",
   425  			badOrdering: false,
   426  			tables: []testTable{
   427  				{
   428  					level:   0,
   429  					fileNum: 19,
   430  					data: []string{
   431  						"a.SET.101 a",
   432  					},
   433  				},
   434  				{
   435  					level:   0,
   436  					fileNum: 20,
   437  					data: []string{
   438  						"a.SET.101 a",
   439  					},
   440  				},
   441  			},
   442  		},
   443  
   444  		{
   445  			description: "broken invariants 3: level non-0 overlapping internal key ranges",
   446  			badOrdering: true,
   447  			tables: []testTable{
   448  				{
   449  					level:   5,
   450  					fileNum: 11,
   451  					data: []string{
   452  						"bat.SET.101 xxx",
   453  						"dog.SET.102 xxx",
   454  					},
   455  				},
   456  				{
   457  					level:   5,
   458  					fileNum: 12,
   459  					data: []string{
   460  						"cow.SET.103 xxx",
   461  						"pig.SET.104 xxx",
   462  					},
   463  				},
   464  			},
   465  		},
   466  	}
   467  
   468  	cmp := testkeys.Comparer.Compare
   469  	for _, tc := range testCases {
   470  		desc := tc.description[:strings.Index(tc.description, ":")]
   471  
   472  		// m is a map from file numbers to DBs.
   473  		m := map[FileNum]*memTable{}
   474  		newIter := func(
   475  			_ context.Context, file *manifest.FileMetadata, _ *IterOptions, _ internalIterOpts,
   476  		) (internalIterator, keyspan.FragmentIterator, error) {
   477  			d, ok := m[file.FileNum]
   478  			if !ok {
   479  				return nil, nil, errors.New("no such file")
   480  			}
   481  			return d.newIter(nil), nil, nil
   482  		}
   483  
   484  		var files [numLevels][]*fileMetadata
   485  		for _, tt := range tc.tables {
   486  			d := newMemTable(memTableOptions{})
   487  			m[tt.fileNum] = d
   488  
   489  			meta := &fileMetadata{
   490  				FileNum: tt.fileNum,
   491  			}
   492  			meta.InitPhysicalBacking()
   493  			for i, datum := range tt.data {
   494  				s := strings.Split(datum, " ")
   495  				ikey := base.ParseInternalKey(s[0])
   496  				err := d.set(ikey, []byte(s[1]))
   497  				if err != nil {
   498  					t.Fatalf("desc=%q: memtable Set: %v", desc, err)
   499  				}
   500  
   501  				meta.ExtendPointKeyBounds(cmp, ikey, ikey)
   502  				if i == 0 {
   503  					meta.SmallestSeqNum = ikey.SeqNum()
   504  					meta.LargestSeqNum = ikey.SeqNum()
   505  				} else {
   506  					if meta.SmallestSeqNum > ikey.SeqNum() {
   507  						meta.SmallestSeqNum = ikey.SeqNum()
   508  					}
   509  					if meta.LargestSeqNum < ikey.SeqNum() {
   510  						meta.LargestSeqNum = ikey.SeqNum()
   511  					}
   512  				}
   513  			}
   514  
   515  			files[tt.level] = append(files[tt.level], meta)
   516  		}
   517  		v := manifest.NewVersion(cmp, base.DefaultFormatter, 10<<20, files)
   518  		err := v.CheckOrdering(cmp, base.DefaultFormatter, manifest.AllowSplitUserKeys)
   519  		if tc.badOrdering && err == nil {
   520  			t.Errorf("desc=%q: want bad ordering, got nil error", desc)
   521  			continue
   522  		} else if !tc.badOrdering && err != nil {
   523  			t.Errorf("desc=%q: bad ordering: %v", desc, err)
   524  			continue
   525  		}
   526  
   527  		get := func(v *version, ikey InternalKey) ([]byte, error) {
   528  			var buf struct {
   529  				dbi Iterator
   530  				get getIter
   531  			}
   532  
   533  			get := &buf.get
   534  			get.comparer = testkeys.Comparer
   535  			get.newIters = newIter
   536  			get.key = ikey.UserKey
   537  			get.l0 = v.L0SublevelFiles
   538  			get.version = v
   539  			get.snapshot = ikey.SeqNum() + 1
   540  
   541  			i := &buf.dbi
   542  			i.comparer = *testkeys.Comparer
   543  			i.merge = DefaultMerger.Merge
   544  			i.iter = get
   545  
   546  			defer i.Close()
   547  			if !i.First() {
   548  				err := i.Error()
   549  				if err != nil {
   550  					return nil, err
   551  				}
   552  				return nil, ErrNotFound
   553  			}
   554  			return i.Value(), nil
   555  		}
   556  
   557  		for _, query := range tc.queries {
   558  			s := strings.Split(query, " ")
   559  			ikey := base.ParseInternalKey(s[0])
   560  			value, err := get(v, ikey)
   561  			got, want := "", s[1]
   562  			if err != nil {
   563  				if err != ErrNotFound {
   564  					t.Errorf("desc=%q: query=%q: %v", desc, s[0], err)
   565  					continue
   566  				}
   567  				got = "ErrNotFound"
   568  			} else {
   569  				got = string(value)
   570  			}
   571  			if got != want {
   572  				t.Errorf("desc=%q: query=%q: got %q, want %q", desc, s[0], got, want)
   573  			}
   574  		}
   575  	}
   576  }