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 }