github.com/zuoyebang/bitalostable@v1.0.1-0.20240229032404-e3b99a834294/sstable/block_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 sstable 6 7 import ( 8 "bytes" 9 "fmt" 10 "strconv" 11 "strings" 12 "testing" 13 "time" 14 "unsafe" 15 16 "github.com/stretchr/testify/require" 17 "github.com/zuoyebang/bitalostable/internal/base" 18 "github.com/zuoyebang/bitalostable/internal/datadriven" 19 "golang.org/x/exp/rand" 20 ) 21 22 func ikey(s string) InternalKey { 23 return InternalKey{UserKey: []byte(s)} 24 } 25 26 func TestBlockWriter(t *testing.T) { 27 w := &rawBlockWriter{ 28 blockWriter: blockWriter{restartInterval: 16}, 29 } 30 w.add(ikey("apple"), nil) 31 w.add(ikey("apricot"), nil) 32 w.add(ikey("banana"), nil) 33 block := w.finish() 34 35 expected := []byte( 36 "\x00\x05\x00apple" + 37 "\x02\x05\x00ricot" + 38 "\x00\x06\x00banana" + 39 "\x00\x00\x00\x00\x01\x00\x00\x00") 40 if !bytes.Equal(expected, block) { 41 t.Fatalf("expected\n%q\nfound\n%q", expected, block) 42 } 43 } 44 45 func testBlockCleared(t *testing.T, w, b *blockWriter) { 46 require.Equal(t, w.restartInterval, b.restartInterval) 47 require.Equal(t, w.nEntries, b.nEntries) 48 require.Equal(t, w.nextRestart, b.nextRestart) 49 require.Equal(t, len(w.buf), len(b.buf)) 50 require.Equal(t, len(w.restarts), len(b.restarts)) 51 require.Equal(t, len(w.curKey), len(b.curKey)) 52 require.Equal(t, len(w.prevKey), len(b.prevKey)) 53 require.Equal(t, len(w.curValue), len(b.curValue)) 54 require.Equal(t, w.tmp, b.tmp) 55 56 // Make sure that we didn't lose the allocated byte slices. 57 require.True(t, cap(w.buf) > 0 && cap(b.buf) == 0) 58 require.True(t, cap(w.restarts) > 0 && cap(b.restarts) == 0) 59 require.True(t, cap(w.curKey) > 0 && cap(b.curKey) == 0) 60 require.True(t, cap(w.prevKey) > 0 && cap(b.prevKey) == 0) 61 require.True(t, cap(w.curValue) > 0 && cap(b.curValue) == 0) 62 } 63 64 func TestBlockClear(t *testing.T) { 65 w := blockWriter{restartInterval: 16} 66 w.add(ikey("apple"), nil) 67 w.add(ikey("apricot"), nil) 68 w.add(ikey("banana"), nil) 69 70 w.clear() 71 72 // Once a block is cleared, we expect its fields to be cleared, but we expect 73 // it to keep its allocated byte slices. 74 b := blockWriter{} 75 testBlockCleared(t, &w, &b) 76 } 77 78 func TestInvalidInternalKeyDecoding(t *testing.T) { 79 // Invalid keys since they don't have an 8 byte trailer. 80 testCases := []string{ 81 "", 82 "\x01\x02\x03\x04\x05\x06\x07", 83 "foo", 84 } 85 for _, tc := range testCases { 86 i := blockIter{} 87 i.decodeInternalKey([]byte(tc)) 88 require.Nil(t, i.ikey.UserKey) 89 require.Equal(t, uint64(InternalKeyKindInvalid), i.ikey.Trailer) 90 } 91 } 92 93 func TestBlockIter(t *testing.T) { 94 // k is a block that maps three keys "apple", "apricot", "banana" to empty strings. 95 k := block([]byte( 96 "\x00\x05\x00apple" + 97 "\x02\x05\x00ricot" + 98 "\x00\x06\x00banana" + 99 "\x00\x00\x00\x00\x01\x00\x00\x00")) 100 var testcases = []struct { 101 index int 102 key string 103 }{ 104 {0, ""}, 105 {0, "a"}, 106 {0, "aaaaaaaaaaaaaaa"}, 107 {0, "app"}, 108 {0, "apple"}, 109 {1, "appliance"}, 110 {1, "apricos"}, 111 {1, "apricot"}, 112 {2, "azzzzzzzzzzzzzz"}, 113 {2, "b"}, 114 {2, "banan"}, 115 {2, "banana"}, 116 {3, "banana\x00"}, 117 {3, "c"}, 118 } 119 for _, tc := range testcases { 120 i, err := newRawBlockIter(bytes.Compare, k) 121 require.NoError(t, err) 122 i.SeekGE([]byte(tc.key)) 123 for j, keyWant := range []string{"apple", "apricot", "banana"}[tc.index:] { 124 if !i.Valid() { 125 t.Fatalf("key=%q, index=%d, j=%d: Valid got false, keyWant true", tc.key, tc.index, j) 126 } 127 if keyGot := string(i.Key().UserKey); keyGot != keyWant { 128 t.Fatalf("key=%q, index=%d, j=%d: got %q, keyWant %q", tc.key, tc.index, j, keyGot, keyWant) 129 } 130 i.Next() 131 } 132 if i.Valid() { 133 t.Fatalf("key=%q, index=%d: Valid got true, keyWant false", tc.key, tc.index) 134 } 135 if err := i.Close(); err != nil { 136 t.Fatalf("key=%q, index=%d: got err=%v", tc.key, tc.index, err) 137 } 138 } 139 140 { 141 i, err := newRawBlockIter(bytes.Compare, k) 142 require.NoError(t, err) 143 i.Last() 144 for j, keyWant := range []string{"banana", "apricot", "apple"} { 145 if !i.Valid() { 146 t.Fatalf("j=%d: Valid got false, want true", j) 147 } 148 if keyGot := string(i.Key().UserKey); keyGot != keyWant { 149 t.Fatalf("j=%d: got %q, want %q", j, keyGot, keyWant) 150 } 151 i.Prev() 152 } 153 if i.Valid() { 154 t.Fatalf("Valid got true, want false") 155 } 156 if err := i.Close(); err != nil { 157 t.Fatalf("got err=%v", err) 158 } 159 } 160 } 161 162 func TestBlockIter2(t *testing.T) { 163 makeIkey := func(s string) InternalKey { 164 j := strings.Index(s, ":") 165 seqNum, err := strconv.Atoi(s[j+1:]) 166 if err != nil { 167 panic(err) 168 } 169 return base.MakeInternalKey([]byte(s[:j]), uint64(seqNum), InternalKeyKindSet) 170 } 171 172 var block []byte 173 174 for _, r := range []int{1, 2, 3, 4} { 175 t.Run(fmt.Sprintf("restart=%d", r), func(t *testing.T) { 176 datadriven.RunTest(t, "testdata/block", func(d *datadriven.TestData) string { 177 switch d.Cmd { 178 case "build": 179 w := &blockWriter{restartInterval: r} 180 for _, e := range strings.Split(strings.TrimSpace(d.Input), ",") { 181 w.add(makeIkey(e), nil) 182 } 183 block = w.finish() 184 return "" 185 186 case "iter": 187 iter, err := newBlockIter(bytes.Compare, block) 188 if err != nil { 189 return err.Error() 190 } 191 192 iter.globalSeqNum, err = scanGlobalSeqNum(d) 193 if err != nil { 194 return err.Error() 195 } 196 197 var b bytes.Buffer 198 for _, line := range strings.Split(d.Input, "\n") { 199 parts := strings.Fields(line) 200 if len(parts) == 0 { 201 continue 202 } 203 switch parts[0] { 204 case "seek-ge": 205 if len(parts) != 2 { 206 return "seek-ge <key>\n" 207 } 208 iter.SeekGE([]byte(strings.TrimSpace(parts[1])), base.SeekGEFlagsNone) 209 case "seek-lt": 210 if len(parts) != 2 { 211 return "seek-lt <key>\n" 212 } 213 iter.SeekLT([]byte(strings.TrimSpace(parts[1])), base.SeekLTFlagsNone) 214 case "first": 215 iter.First() 216 case "last": 217 iter.Last() 218 case "next": 219 iter.Next() 220 case "prev": 221 iter.Prev() 222 } 223 if iter.valid() { 224 fmt.Fprintf(&b, "<%s:%d>", iter.Key().UserKey, iter.Key().SeqNum()) 225 } else if err := iter.Error(); err != nil { 226 fmt.Fprintf(&b, "<err=%v>", err) 227 } else { 228 fmt.Fprintf(&b, ".") 229 } 230 } 231 b.WriteString("\n") 232 return b.String() 233 234 default: 235 return fmt.Sprintf("unknown command: %s", d.Cmd) 236 } 237 }) 238 }) 239 } 240 } 241 242 func TestBlockIterKeyStability(t *testing.T) { 243 w := &blockWriter{restartInterval: 1} 244 expected := [][]byte{ 245 []byte("apple"), 246 []byte("apricot"), 247 []byte("banana"), 248 } 249 for i := range expected { 250 w.add(InternalKey{UserKey: expected[i]}, nil) 251 } 252 block := w.finish() 253 254 i, err := newBlockIter(bytes.Compare, block) 255 require.NoError(t, err) 256 257 // Check that the supplied slice resides within the bounds of the block. 258 check := func(v []byte) { 259 t.Helper() 260 begin := unsafe.Pointer(&v[0]) 261 end := unsafe.Pointer(uintptr(begin) + uintptr(len(v))) 262 blockBegin := unsafe.Pointer(&block[0]) 263 blockEnd := unsafe.Pointer(uintptr(blockBegin) + uintptr(len(block))) 264 if uintptr(begin) < uintptr(blockBegin) || uintptr(end) > uintptr(blockEnd) { 265 t.Fatalf("key %p-%p resides outside of block %p-%p", begin, end, blockBegin, blockEnd) 266 } 267 } 268 269 // Check that various means of iterating over the data match our expected 270 // values. Note that this is only guaranteed because of the usage of a 271 // restart-interval of 1 so that prefix compression was not performed. 272 for j := range expected { 273 keys := [][]byte{} 274 for key, _ := i.SeekGE(expected[j], base.SeekGEFlagsNone); key != nil; key, _ = i.Next() { 275 check(key.UserKey) 276 keys = append(keys, key.UserKey) 277 } 278 require.EqualValues(t, expected[j:], keys) 279 } 280 281 for j := range expected { 282 keys := [][]byte{} 283 for key, _ := i.SeekLT(expected[j], base.SeekLTFlagsNone); key != nil; key, _ = i.Prev() { 284 check(key.UserKey) 285 keys = append(keys, key.UserKey) 286 } 287 for i, j := 0, len(keys)-1; i < j; i, j = i+1, j-1 { 288 keys[i], keys[j] = keys[j], keys[i] 289 } 290 require.EqualValues(t, expected[:j], keys) 291 } 292 } 293 294 // Regression test for a bug in blockIter.Next where it was failing to handle 295 // the case where it is switching from reverse to forward iteration. When that 296 // switch occurs we need to populate blockIter.fullKey so that prefix 297 // decompression works properly. 298 func TestBlockIterReverseDirections(t *testing.T) { 299 w := &blockWriter{restartInterval: 4} 300 keys := [][]byte{ 301 []byte("apple0"), 302 []byte("apple1"), 303 []byte("apple2"), 304 []byte("banana"), 305 []byte("carrot"), 306 } 307 for i := range keys { 308 w.add(InternalKey{UserKey: keys[i]}, nil) 309 } 310 block := w.finish() 311 312 for targetPos := 0; targetPos < w.restartInterval; targetPos++ { 313 t.Run("", func(t *testing.T) { 314 i, err := newBlockIter(bytes.Compare, block) 315 require.NoError(t, err) 316 317 pos := 3 318 if key, _ := i.SeekLT([]byte("carrot"), base.SeekLTFlagsNone); !bytes.Equal(keys[pos], key.UserKey) { 319 t.Fatalf("expected %s, but found %s", keys[pos], key.UserKey) 320 } 321 for pos > targetPos { 322 pos-- 323 if key, _ := i.Prev(); !bytes.Equal(keys[pos], key.UserKey) { 324 t.Fatalf("expected %s, but found %s", keys[pos], key.UserKey) 325 } 326 } 327 pos++ 328 if key, _ := i.Next(); !bytes.Equal(keys[pos], key.UserKey) { 329 t.Fatalf("expected %s, but found %s", keys[pos], key.UserKey) 330 } 331 }) 332 } 333 } 334 335 func BenchmarkBlockIterSeekGE(b *testing.B) { 336 const blockSize = 32 << 10 337 338 for _, restartInterval := range []int{16} { 339 b.Run(fmt.Sprintf("restart=%d", restartInterval), 340 func(b *testing.B) { 341 w := &blockWriter{ 342 restartInterval: restartInterval, 343 } 344 345 var ikey InternalKey 346 var keys [][]byte 347 for i := 0; w.estimatedSize() < blockSize; i++ { 348 key := []byte(fmt.Sprintf("%05d", i)) 349 keys = append(keys, key) 350 ikey.UserKey = key 351 w.add(ikey, nil) 352 } 353 354 it, err := newBlockIter(bytes.Compare, w.finish()) 355 if err != nil { 356 b.Fatal(err) 357 } 358 rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano()))) 359 360 b.ResetTimer() 361 for i := 0; i < b.N; i++ { 362 k := keys[rng.Intn(len(keys))] 363 it.SeekGE(k, base.SeekGEFlagsNone) 364 if testing.Verbose() { 365 if !it.valid() { 366 b.Fatal("expected to find key") 367 } 368 if !bytes.Equal(k, it.Key().UserKey) { 369 b.Fatalf("expected %s, but found %s", k, it.Key().UserKey) 370 } 371 } 372 } 373 }) 374 } 375 } 376 377 func BenchmarkBlockIterSeekLT(b *testing.B) { 378 const blockSize = 32 << 10 379 380 for _, restartInterval := range []int{16} { 381 b.Run(fmt.Sprintf("restart=%d", restartInterval), 382 func(b *testing.B) { 383 w := &blockWriter{ 384 restartInterval: restartInterval, 385 } 386 387 var ikey InternalKey 388 var keys [][]byte 389 for i := 0; w.estimatedSize() < blockSize; i++ { 390 key := []byte(fmt.Sprintf("%05d", i)) 391 keys = append(keys, key) 392 ikey.UserKey = key 393 w.add(ikey, nil) 394 } 395 396 it, err := newBlockIter(bytes.Compare, w.finish()) 397 if err != nil { 398 b.Fatal(err) 399 } 400 rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano()))) 401 402 b.ResetTimer() 403 for i := 0; i < b.N; i++ { 404 j := rng.Intn(len(keys)) 405 it.SeekLT(keys[j], base.SeekLTFlagsNone) 406 if testing.Verbose() { 407 if j == 0 { 408 if it.valid() { 409 b.Fatal("unexpected key") 410 } 411 } else { 412 if !it.valid() { 413 b.Fatal("expected to find key") 414 } 415 k := keys[j-1] 416 if !bytes.Equal(k, it.Key().UserKey) { 417 b.Fatalf("expected %s, but found %s", k, it.Key().UserKey) 418 } 419 } 420 } 421 } 422 }) 423 } 424 } 425 426 func BenchmarkBlockIterNext(b *testing.B) { 427 const blockSize = 32 << 10 428 429 for _, restartInterval := range []int{16} { 430 b.Run(fmt.Sprintf("restart=%d", restartInterval), 431 func(b *testing.B) { 432 w := &blockWriter{ 433 restartInterval: restartInterval, 434 } 435 436 var ikey InternalKey 437 for i := 0; w.estimatedSize() < blockSize; i++ { 438 ikey.UserKey = []byte(fmt.Sprintf("%05d", i)) 439 w.add(ikey, nil) 440 } 441 442 it, err := newBlockIter(bytes.Compare, w.finish()) 443 if err != nil { 444 b.Fatal(err) 445 } 446 447 b.ResetTimer() 448 for i := 0; i < b.N; i++ { 449 if !it.valid() { 450 it.First() 451 } 452 it.Next() 453 } 454 }) 455 } 456 } 457 458 func BenchmarkBlockIterPrev(b *testing.B) { 459 const blockSize = 32 << 10 460 461 for _, restartInterval := range []int{16} { 462 b.Run(fmt.Sprintf("restart=%d", restartInterval), 463 func(b *testing.B) { 464 w := &blockWriter{ 465 restartInterval: restartInterval, 466 } 467 468 var ikey InternalKey 469 for i := 0; w.estimatedSize() < blockSize; i++ { 470 ikey.UserKey = []byte(fmt.Sprintf("%05d", i)) 471 w.add(ikey, nil) 472 } 473 474 it, err := newBlockIter(bytes.Compare, w.finish()) 475 if err != nil { 476 b.Fatal(err) 477 } 478 479 b.ResetTimer() 480 for i := 0; i < b.N; i++ { 481 if !it.valid() { 482 it.Last() 483 } 484 it.Prev() 485 } 486 }) 487 } 488 }