github.com/ethereum/go-ethereum@v1.16.1/ethdb/dbtest/testsuite.go (about) 1 // Copyright 2019 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package dbtest 18 19 import ( 20 "bytes" 21 "crypto/rand" 22 "slices" 23 "sort" 24 "strconv" 25 "testing" 26 27 "github.com/ethereum/go-ethereum/ethdb" 28 ) 29 30 // TestDatabaseSuite runs a suite of tests against a KeyValueStore database 31 // implementation. 32 func TestDatabaseSuite(t *testing.T, New func() ethdb.KeyValueStore) { 33 t.Run("Iterator", func(t *testing.T) { 34 tests := []struct { 35 content map[string]string 36 prefix string 37 start string 38 order []string 39 }{ 40 // Empty databases should be iterable 41 {map[string]string{}, "", "", nil}, 42 {map[string]string{}, "non-existent-prefix", "", nil}, 43 44 // Single-item databases should be iterable 45 {map[string]string{"key": "val"}, "", "", []string{"key"}}, 46 {map[string]string{"key": "val"}, "k", "", []string{"key"}}, 47 {map[string]string{"key": "val"}, "l", "", nil}, 48 49 // Multi-item databases should be fully iterable 50 { 51 map[string]string{"k1": "v1", "k5": "v5", "k2": "v2", "k4": "v4", "k3": "v3"}, 52 "", "", 53 []string{"k1", "k2", "k3", "k4", "k5"}, 54 }, 55 { 56 map[string]string{"k1": "v1", "k5": "v5", "k2": "v2", "k4": "v4", "k3": "v3"}, 57 "k", "", 58 []string{"k1", "k2", "k3", "k4", "k5"}, 59 }, 60 { 61 map[string]string{"k1": "v1", "k5": "v5", "k2": "v2", "k4": "v4", "k3": "v3"}, 62 "l", "", 63 nil, 64 }, 65 // Multi-item databases should be prefix-iterable 66 { 67 map[string]string{ 68 "ka1": "va1", "ka5": "va5", "ka2": "va2", "ka4": "va4", "ka3": "va3", 69 "kb1": "vb1", "kb5": "vb5", "kb2": "vb2", "kb4": "vb4", "kb3": "vb3", 70 }, 71 "ka", "", 72 []string{"ka1", "ka2", "ka3", "ka4", "ka5"}, 73 }, 74 { 75 map[string]string{ 76 "ka1": "va1", "ka5": "va5", "ka2": "va2", "ka4": "va4", "ka3": "va3", 77 "kb1": "vb1", "kb5": "vb5", "kb2": "vb2", "kb4": "vb4", "kb3": "vb3", 78 }, 79 "kc", "", 80 nil, 81 }, 82 // Multi-item databases should be prefix-iterable with start position 83 { 84 map[string]string{ 85 "ka1": "va1", "ka5": "va5", "ka2": "va2", "ka4": "va4", "ka3": "va3", 86 "kb1": "vb1", "kb5": "vb5", "kb2": "vb2", "kb4": "vb4", "kb3": "vb3", 87 }, 88 "ka", "3", 89 []string{"ka3", "ka4", "ka5"}, 90 }, 91 { 92 map[string]string{ 93 "ka1": "va1", "ka5": "va5", "ka2": "va2", "ka4": "va4", "ka3": "va3", 94 "kb1": "vb1", "kb5": "vb5", "kb2": "vb2", "kb4": "vb4", "kb3": "vb3", 95 }, 96 "ka", "8", 97 nil, 98 }, 99 } 100 for i, tt := range tests { 101 // Create the key-value data store 102 db := New() 103 for key, val := range tt.content { 104 if err := db.Put([]byte(key), []byte(val)); err != nil { 105 t.Fatalf("test %d: failed to insert item %s:%s into database: %v", i, key, val, err) 106 } 107 } 108 // Iterate over the database with the given configs and verify the results 109 it, idx := db.NewIterator([]byte(tt.prefix), []byte(tt.start)), 0 110 for it.Next() { 111 if len(tt.order) <= idx { 112 t.Errorf("test %d: prefix=%q more items than expected: checking idx=%d (key %q), expecting len=%d", i, tt.prefix, idx, it.Key(), len(tt.order)) 113 break 114 } 115 if !bytes.Equal(it.Key(), []byte(tt.order[idx])) { 116 t.Errorf("test %d: item %d: key mismatch: have %s, want %s", i, idx, string(it.Key()), tt.order[idx]) 117 } 118 if !bytes.Equal(it.Value(), []byte(tt.content[tt.order[idx]])) { 119 t.Errorf("test %d: item %d: value mismatch: have %s, want %s", i, idx, string(it.Value()), tt.content[tt.order[idx]]) 120 } 121 idx++ 122 } 123 if err := it.Error(); err != nil { 124 t.Errorf("test %d: iteration failed: %v", i, err) 125 } 126 if idx != len(tt.order) { 127 t.Errorf("test %d: iteration terminated prematurely: have %d, want %d", i, idx, len(tt.order)) 128 } 129 db.Close() 130 } 131 }) 132 133 t.Run("IteratorWith", func(t *testing.T) { 134 db := New() 135 defer db.Close() 136 137 keys := []string{"1", "2", "3", "4", "6", "10", "11", "12", "20", "21", "22"} 138 sort.Strings(keys) // 1, 10, 11, etc 139 140 for _, k := range keys { 141 if err := db.Put([]byte(k), nil); err != nil { 142 t.Fatal(err) 143 } 144 } 145 146 { 147 it := db.NewIterator(nil, nil) 148 got, want := iterateKeys(it), keys 149 if err := it.Error(); err != nil { 150 t.Fatal(err) 151 } 152 if !slices.Equal(got, want) { 153 t.Errorf("Iterator: got: %s; want: %s", got, want) 154 } 155 } 156 157 { 158 it := db.NewIterator([]byte("1"), nil) 159 got, want := iterateKeys(it), []string{"1", "10", "11", "12"} 160 if err := it.Error(); err != nil { 161 t.Fatal(err) 162 } 163 if !slices.Equal(got, want) { 164 t.Errorf("IteratorWith(1,nil): got: %s; want: %s", got, want) 165 } 166 } 167 168 { 169 it := db.NewIterator([]byte("5"), nil) 170 got, want := iterateKeys(it), []string{} 171 if err := it.Error(); err != nil { 172 t.Fatal(err) 173 } 174 if !slices.Equal(got, want) { 175 t.Errorf("IteratorWith(5,nil): got: %s; want: %s", got, want) 176 } 177 } 178 179 { 180 it := db.NewIterator(nil, []byte("2")) 181 got, want := iterateKeys(it), []string{"2", "20", "21", "22", "3", "4", "6"} 182 if err := it.Error(); err != nil { 183 t.Fatal(err) 184 } 185 if !slices.Equal(got, want) { 186 t.Errorf("IteratorWith(nil,2): got: %s; want: %s", got, want) 187 } 188 } 189 190 { 191 it := db.NewIterator(nil, []byte("5")) 192 got, want := iterateKeys(it), []string{"6"} 193 if err := it.Error(); err != nil { 194 t.Fatal(err) 195 } 196 if !slices.Equal(got, want) { 197 t.Errorf("IteratorWith(nil,5): got: %s; want: %s", got, want) 198 } 199 } 200 }) 201 202 t.Run("KeyValueOperations", func(t *testing.T) { 203 db := New() 204 defer db.Close() 205 206 key := []byte("foo") 207 208 if got, err := db.Has(key); err != nil { 209 t.Error(err) 210 } else if got { 211 t.Errorf("wrong value: %t", got) 212 } 213 214 value := []byte("hello world") 215 if err := db.Put(key, value); err != nil { 216 t.Error(err) 217 } 218 219 if got, err := db.Has(key); err != nil { 220 t.Error(err) 221 } else if !got { 222 t.Errorf("wrong value: %t", got) 223 } 224 225 if got, err := db.Get(key); err != nil { 226 t.Error(err) 227 } else if !bytes.Equal(got, value) { 228 t.Errorf("wrong value: %q", got) 229 } 230 231 if err := db.Delete(key); err != nil { 232 t.Error(err) 233 } 234 235 if got, err := db.Has(key); err != nil { 236 t.Error(err) 237 } else if got { 238 t.Errorf("wrong value: %t", got) 239 } 240 }) 241 242 t.Run("Batch", func(t *testing.T) { 243 db := New() 244 defer db.Close() 245 246 b := db.NewBatch() 247 for _, k := range []string{"1", "2", "3", "4"} { 248 if err := b.Put([]byte(k), nil); err != nil { 249 t.Fatal(err) 250 } 251 } 252 253 if has, err := db.Has([]byte("1")); err != nil { 254 t.Fatal(err) 255 } else if has { 256 t.Error("db contains element before batch write") 257 } 258 259 if err := b.Write(); err != nil { 260 t.Fatal(err) 261 } 262 263 { 264 it := db.NewIterator(nil, nil) 265 if got, want := iterateKeys(it), []string{"1", "2", "3", "4"}; !slices.Equal(got, want) { 266 t.Errorf("got: %s; want: %s", got, want) 267 } 268 } 269 270 b.Reset() 271 272 // Mix writes and deletes in batch 273 b.Put([]byte("5"), nil) 274 b.Delete([]byte("1")) 275 b.Put([]byte("6"), nil) 276 277 b.Delete([]byte("3")) // delete then put 278 b.Put([]byte("3"), nil) 279 280 b.Put([]byte("7"), nil) // put then delete 281 b.Delete([]byte("7")) 282 283 if err := b.Write(); err != nil { 284 t.Fatal(err) 285 } 286 287 { 288 it := db.NewIterator(nil, nil) 289 if got, want := iterateKeys(it), []string{"2", "3", "4", "5", "6"}; !slices.Equal(got, want) { 290 t.Errorf("got: %s; want: %s", got, want) 291 } 292 } 293 }) 294 295 t.Run("BatchReplay", func(t *testing.T) { 296 db := New() 297 defer db.Close() 298 299 want := []string{"1", "2", "3", "4"} 300 b := db.NewBatch() 301 for _, k := range want { 302 if err := b.Put([]byte(k), nil); err != nil { 303 t.Fatal(err) 304 } 305 } 306 307 b2 := db.NewBatch() 308 if err := b.Replay(b2); err != nil { 309 t.Fatal(err) 310 } 311 312 if err := b2.Replay(db); err != nil { 313 t.Fatal(err) 314 } 315 316 it := db.NewIterator(nil, nil) 317 if got := iterateKeys(it); !slices.Equal(got, want) { 318 t.Errorf("got: %s; want: %s", got, want) 319 } 320 }) 321 322 t.Run("OperationsAfterClose", func(t *testing.T) { 323 db := New() 324 db.Put([]byte("key"), []byte("value")) 325 db.Close() 326 if _, err := db.Get([]byte("key")); err == nil { 327 t.Fatalf("expected error on Get after Close") 328 } 329 if _, err := db.Has([]byte("key")); err == nil { 330 t.Fatalf("expected error on Get after Close") 331 } 332 if err := db.Put([]byte("key2"), []byte("value2")); err == nil { 333 t.Fatalf("expected error on Put after Close") 334 } 335 if err := db.Delete([]byte("key")); err == nil { 336 t.Fatalf("expected error on Delete after Close") 337 } 338 339 b := db.NewBatch() 340 if err := b.Put([]byte("batchkey"), []byte("batchval")); err != nil { 341 t.Fatalf("expected no error on batch.Put after Close, got %v", err) 342 } 343 if err := b.Write(); err == nil { 344 t.Fatalf("expected error on batch.Write after Close") 345 } 346 }) 347 348 t.Run("DeleteRange", func(t *testing.T) { 349 db := New() 350 defer db.Close() 351 352 addRange := func(start, stop int) { 353 for i := start; i <= stop; i++ { 354 db.Put([]byte(strconv.Itoa(i)), nil) 355 } 356 } 357 358 checkRange := func(start, stop int, exp bool) { 359 for i := start; i <= stop; i++ { 360 has, _ := db.Has([]byte(strconv.Itoa(i))) 361 if has && !exp { 362 t.Fatalf("unexpected key %d", i) 363 } 364 if !has && exp { 365 t.Fatalf("missing expected key %d", i) 366 } 367 } 368 } 369 370 addRange(1, 9) 371 db.DeleteRange([]byte("9"), []byte("1")) 372 checkRange(1, 9, true) 373 db.DeleteRange([]byte("5"), []byte("5")) 374 checkRange(1, 9, true) 375 db.DeleteRange([]byte("5"), []byte("50")) 376 checkRange(1, 4, true) 377 checkRange(5, 5, false) 378 checkRange(6, 9, true) 379 db.DeleteRange([]byte(""), []byte("a")) 380 checkRange(1, 9, false) 381 382 addRange(1, 999) 383 db.DeleteRange([]byte("12345"), []byte("54321")) 384 checkRange(1, 1, true) 385 checkRange(2, 5, false) 386 checkRange(6, 12, true) 387 checkRange(13, 54, false) 388 checkRange(55, 123, true) 389 checkRange(124, 543, false) 390 checkRange(544, 999, true) 391 392 addRange(1, 999) 393 db.DeleteRange([]byte("3"), []byte("7")) 394 checkRange(1, 2, true) 395 checkRange(3, 6, false) 396 checkRange(7, 29, true) 397 checkRange(30, 69, false) 398 checkRange(70, 299, true) 399 checkRange(300, 699, false) 400 checkRange(700, 999, true) 401 402 db.DeleteRange([]byte(""), []byte("a")) 403 checkRange(1, 999, false) 404 405 addRange(1, 999) 406 db.DeleteRange(nil, nil) 407 checkRange(1, 999, false) 408 }) 409 410 t.Run("BatchDeleteRange", func(t *testing.T) { 411 db := New() 412 defer db.Close() 413 414 // Helper to add keys 415 addKeys := func(start, stop int) { 416 for i := start; i <= stop; i++ { 417 if err := db.Put([]byte(strconv.Itoa(i)), []byte("val-"+strconv.Itoa(i))); err != nil { 418 t.Fatal(err) 419 } 420 } 421 } 422 423 // Helper to check if keys exist 424 checkKeys := func(start, stop int, shouldExist bool) { 425 for i := start; i <= stop; i++ { 426 key := []byte(strconv.Itoa(i)) 427 has, err := db.Has(key) 428 if err != nil { 429 t.Fatal(err) 430 } 431 if has != shouldExist { 432 if shouldExist { 433 t.Fatalf("key %s should exist but doesn't", key) 434 } else { 435 t.Fatalf("key %s shouldn't exist but does", key) 436 } 437 } 438 } 439 } 440 441 // Test 1: Basic range deletion in batch 442 addKeys(1, 10) 443 checkKeys(1, 10, true) 444 445 batch := db.NewBatch() 446 if err := batch.DeleteRange([]byte("3"), []byte("8")); err != nil { 447 t.Fatal(err) 448 } 449 // Keys shouldn't be deleted until Write is called 450 checkKeys(1, 10, true) 451 452 if err := batch.Write(); err != nil { 453 t.Fatal(err) 454 } 455 // After Write, keys in range should be deleted 456 // Range is [start, end) - inclusive of start, exclusive of end 457 checkKeys(1, 2, true) // These should still exist 458 checkKeys(3, 7, false) // These should be deleted (3 to 7 inclusive) 459 checkKeys(8, 10, true) // These should still exist (8 is the end boundary, exclusive) 460 461 // Test 2: Delete range with special markers 462 addKeys(3, 7) 463 batch = db.NewBatch() 464 if err := batch.DeleteRange(nil, nil); err != nil { 465 t.Fatal(err) 466 } 467 if err := batch.Write(); err != nil { 468 t.Fatal(err) 469 } 470 checkKeys(1, 10, false) 471 472 // Test 3: Mix Put, Delete, and DeleteRange in a batch 473 // Reset database for next test by adding back deleted keys 474 addKeys(1, 10) 475 checkKeys(1, 10, true) 476 477 // Create a new batch with multiple operations 478 batch = db.NewBatch() 479 if err := batch.Put([]byte("5"), []byte("new-val-5")); err != nil { 480 t.Fatal(err) 481 } 482 if err := batch.Delete([]byte("9")); err != nil { 483 t.Fatal(err) 484 } 485 if err := batch.DeleteRange([]byte("1"), []byte("3")); err != nil { 486 t.Fatal(err) 487 } 488 if err := batch.Write(); err != nil { 489 t.Fatal(err) 490 } 491 // Check results after batch operations 492 // Keys 1-2 should be deleted by DeleteRange 493 checkKeys(1, 2, false) 494 495 // Key 3 should exist (exclusive of end) 496 has, err := db.Has([]byte("3")) 497 if err != nil { 498 t.Fatal(err) 499 } 500 if !has { 501 t.Fatalf("key 3 should exist after DeleteRange(1,3)") 502 } 503 504 // Key 5 should have a new value 505 val, err := db.Get([]byte("5")) 506 if err != nil { 507 t.Fatal(err) 508 } 509 if !bytes.Equal(val, []byte("new-val-5")) { 510 t.Fatalf("key 5 has wrong value: got %s, want %s", val, "new-val-5") 511 } 512 513 // Key 9 should be deleted 514 has, err = db.Has([]byte("9")) 515 if err != nil { 516 t.Fatal(err) 517 } 518 if has { 519 t.Fatalf("key 9 should be deleted") 520 } 521 522 // Test 4: Reset batch 523 batch.Reset() 524 // Individual deletes work better with both string and numeric comparisons 525 if err := batch.Delete([]byte("8")); err != nil { 526 t.Fatal(err) 527 } 528 if err := batch.Delete([]byte("10")); err != nil { 529 t.Fatal(err) 530 } 531 if err := batch.Delete([]byte("11")); err != nil { 532 t.Fatal(err) 533 } 534 if err := batch.Write(); err != nil { 535 t.Fatal(err) 536 } 537 538 // Key 8 should be deleted 539 has, err = db.Has([]byte("8")) 540 if err != nil { 541 t.Fatal(err) 542 } 543 if has { 544 t.Fatalf("key 8 should be deleted") 545 } 546 547 // Keys 3-7 should still exist 548 checkKeys(3, 7, true) 549 550 // Key 10 should be deleted 551 has, err = db.Has([]byte("10")) 552 if err != nil { 553 t.Fatal(err) 554 } 555 if has { 556 t.Fatalf("key 10 should be deleted") 557 } 558 559 // Test 5: Empty range 560 batch = db.NewBatch() 561 if err := batch.DeleteRange([]byte("100"), []byte("100")); err != nil { 562 t.Fatal(err) 563 } 564 if err := batch.Write(); err != nil { 565 t.Fatal(err) 566 } 567 // No existing keys should be affected 568 checkKeys(3, 7, true) 569 570 // Test 6: Test entire keyspace deletion 571 // First clear any existing keys 572 for i := 1; i <= 100; i++ { 573 db.Delete([]byte(strconv.Itoa(i))) 574 } 575 576 // Then add some fresh test keys 577 addKeys(50, 60) 578 579 // Verify keys exist before deletion 580 checkKeys(50, 60, true) 581 582 batch = db.NewBatch() 583 if err := batch.DeleteRange([]byte(""), []byte("z")); err != nil { 584 t.Fatal(err) 585 } 586 if err := batch.Write(); err != nil { 587 t.Fatal(err) 588 } 589 // All keys should be deleted 590 checkKeys(50, 60, false) 591 592 // Test 7: overlapping range deletion 593 addKeys(50, 60) 594 batch = db.NewBatch() 595 if err := batch.DeleteRange([]byte("50"), []byte("55")); err != nil { 596 t.Fatal(err) 597 } 598 if err := batch.DeleteRange([]byte("52"), []byte("58")); err != nil { 599 t.Fatal(err) 600 } 601 if err := batch.Write(); err != nil { 602 t.Fatal(err) 603 } 604 checkKeys(50, 57, false) 605 checkKeys(58, 60, true) 606 }) 607 608 t.Run("BatchReplayWithDeleteRange", func(t *testing.T) { 609 db := New() 610 defer db.Close() 611 612 // Setup some initial data 613 for i := 1; i <= 10; i++ { 614 if err := db.Put([]byte(strconv.Itoa(i)), []byte("val-"+strconv.Itoa(i))); err != nil { 615 t.Fatal(err) 616 } 617 } 618 619 // Create batch with multiple operations including DeleteRange 620 batch1 := db.NewBatch() 621 batch1.Put([]byte("new-key-1"), []byte("new-val-1")) 622 batch1.DeleteRange([]byte("3"), []byte("7")) // Should delete keys 3-6 but not 7 623 batch1.Delete([]byte("8")) 624 batch1.Put([]byte("new-key-2"), []byte("new-val-2")) 625 626 // Create a second batch to replay into 627 batch2 := db.NewBatch() 628 if err := batch1.Replay(batch2); err != nil { 629 t.Fatal(err) 630 } 631 632 // Write the second batch 633 if err := batch2.Write(); err != nil { 634 t.Fatal(err) 635 } 636 637 // Verify results 638 // Original keys 3-6 should be deleted (inclusive of start, exclusive of end) 639 for i := 3; i <= 6; i++ { 640 has, err := db.Has([]byte(strconv.Itoa(i))) 641 if err != nil { 642 t.Fatal(err) 643 } 644 if has { 645 t.Fatalf("key %d should be deleted", i) 646 } 647 } 648 649 // Key 7 should NOT be deleted (exclusive of end) 650 has, err := db.Has([]byte("7")) 651 if err != nil { 652 t.Fatal(err) 653 } 654 if !has { 655 t.Fatalf("key 7 should NOT be deleted (exclusive of end)") 656 } 657 658 // Key 8 should be deleted 659 has, err = db.Has([]byte("8")) 660 if err != nil { 661 t.Fatal(err) 662 } 663 if has { 664 t.Fatalf("key 8 should be deleted") 665 } 666 667 // New keys should be added 668 for _, key := range []string{"new-key-1", "new-key-2"} { 669 has, err := db.Has([]byte(key)) 670 if err != nil { 671 t.Fatal(err) 672 } 673 if !has { 674 t.Fatalf("key %s should exist", key) 675 } 676 } 677 678 // Create a third batch for direct replay to database 679 batch3 := db.NewBatch() 680 batch3.DeleteRange([]byte("1"), []byte("3")) // Should delete keys 1-2 but not 3 681 682 // Replay directly to the database 683 if err := batch3.Replay(db); err != nil { 684 t.Fatal(err) 685 } 686 687 // Verify keys 1-2 are now deleted 688 for i := 1; i <= 2; i++ { 689 has, err := db.Has([]byte(strconv.Itoa(i))) 690 if err != nil { 691 t.Fatal(err) 692 } 693 if has { 694 t.Fatalf("key %d should be deleted after direct replay", i) 695 } 696 } 697 698 // Verify key 3 is NOT deleted (since it's exclusive of end) 699 has, err = db.Has([]byte("3")) 700 if err != nil { 701 t.Fatal(err) 702 } 703 if has { 704 t.Fatalf("key 3 should still be deleted from previous operation") 705 } 706 }) 707 } 708 709 // BenchDatabaseSuite runs a suite of benchmarks against a KeyValueStore database 710 // implementation. 711 func BenchDatabaseSuite(b *testing.B, New func() ethdb.KeyValueStore) { 712 var ( 713 keys, vals = makeDataset(1_000_000, 32, 32, false) 714 sKeys, sVals = makeDataset(1_000_000, 32, 32, true) 715 ) 716 // Run benchmarks sequentially 717 b.Run("Write", func(b *testing.B) { 718 benchWrite := func(b *testing.B, keys, vals [][]byte) { 719 b.ResetTimer() 720 b.ReportAllocs() 721 722 db := New() 723 defer db.Close() 724 725 for i := 0; i < len(keys); i++ { 726 db.Put(keys[i], vals[i]) 727 } 728 } 729 b.Run("WriteSorted", func(b *testing.B) { 730 benchWrite(b, sKeys, sVals) 731 }) 732 b.Run("WriteRandom", func(b *testing.B) { 733 benchWrite(b, keys, vals) 734 }) 735 }) 736 b.Run("Read", func(b *testing.B) { 737 benchRead := func(b *testing.B, keys, vals [][]byte) { 738 db := New() 739 defer db.Close() 740 741 for i := 0; i < len(keys); i++ { 742 db.Put(keys[i], vals[i]) 743 } 744 b.ResetTimer() 745 b.ReportAllocs() 746 747 for i := 0; i < len(keys); i++ { 748 db.Get(keys[i]) 749 } 750 } 751 b.Run("ReadSorted", func(b *testing.B) { 752 benchRead(b, sKeys, sVals) 753 }) 754 b.Run("ReadRandom", func(b *testing.B) { 755 benchRead(b, keys, vals) 756 }) 757 }) 758 b.Run("Iteration", func(b *testing.B) { 759 benchIteration := func(b *testing.B, keys, vals [][]byte) { 760 db := New() 761 defer db.Close() 762 763 for i := 0; i < len(keys); i++ { 764 db.Put(keys[i], vals[i]) 765 } 766 b.ResetTimer() 767 b.ReportAllocs() 768 769 it := db.NewIterator(nil, nil) 770 for it.Next() { 771 } 772 it.Release() 773 } 774 b.Run("IterationSorted", func(b *testing.B) { 775 benchIteration(b, sKeys, sVals) 776 }) 777 b.Run("IterationRandom", func(b *testing.B) { 778 benchIteration(b, keys, vals) 779 }) 780 }) 781 b.Run("BatchWrite", func(b *testing.B) { 782 benchBatchWrite := func(b *testing.B, keys, vals [][]byte) { 783 b.ResetTimer() 784 b.ReportAllocs() 785 786 db := New() 787 defer db.Close() 788 789 batch := db.NewBatch() 790 for i := 0; i < len(keys); i++ { 791 batch.Put(keys[i], vals[i]) 792 } 793 batch.Write() 794 } 795 b.Run("BenchWriteSorted", func(b *testing.B) { 796 benchBatchWrite(b, sKeys, sVals) 797 }) 798 b.Run("BenchWriteRandom", func(b *testing.B) { 799 benchBatchWrite(b, keys, vals) 800 }) 801 }) 802 b.Run("DeleteRange", func(b *testing.B) { 803 benchDeleteRange := func(b *testing.B, count int) { 804 db := New() 805 defer db.Close() 806 807 for i := 0; i < count; i++ { 808 db.Put([]byte(strconv.Itoa(i)), nil) 809 } 810 b.ResetTimer() 811 b.ReportAllocs() 812 813 db.DeleteRange([]byte("0"), []byte("999999999")) 814 } 815 b.Run("DeleteRange100", func(b *testing.B) { 816 benchDeleteRange(b, 100) 817 }) 818 b.Run("DeleteRange1k", func(b *testing.B) { 819 benchDeleteRange(b, 1000) 820 }) 821 b.Run("DeleteRange10k", func(b *testing.B) { 822 benchDeleteRange(b, 10000) 823 }) 824 }) 825 b.Run("BatchDeleteRange", func(b *testing.B) { 826 benchBatchDeleteRange := func(b *testing.B, count int) { 827 db := New() 828 defer db.Close() 829 830 // Prepare data 831 for i := 0; i < count; i++ { 832 db.Put([]byte(strconv.Itoa(i)), nil) 833 } 834 835 b.ResetTimer() 836 b.ReportAllocs() 837 838 // Create batch and delete range 839 batch := db.NewBatch() 840 batch.DeleteRange([]byte("0"), []byte("999999999")) 841 batch.Write() 842 } 843 844 b.Run("BatchDeleteRange100", func(b *testing.B) { 845 benchBatchDeleteRange(b, 100) 846 }) 847 b.Run("BatchDeleteRange1k", func(b *testing.B) { 848 benchBatchDeleteRange(b, 1000) 849 }) 850 b.Run("BatchDeleteRange10k", func(b *testing.B) { 851 benchBatchDeleteRange(b, 10000) 852 }) 853 }) 854 855 b.Run("BatchMixedOps", func(b *testing.B) { 856 benchBatchMixedOps := func(b *testing.B, count int) { 857 db := New() 858 defer db.Close() 859 860 // Prepare initial data 861 for i := 0; i < count; i++ { 862 db.Put([]byte(strconv.Itoa(i)), []byte("val")) 863 } 864 865 b.ResetTimer() 866 b.ReportAllocs() 867 868 // Create batch with mixed operations 869 batch := db.NewBatch() 870 871 // Add some new keys 872 for i := 0; i < count/10; i++ { 873 batch.Put([]byte(strconv.Itoa(count+i)), []byte("new-val")) 874 } 875 876 // Delete some individual keys 877 for i := 0; i < count/20; i++ { 878 batch.Delete([]byte(strconv.Itoa(i * 2))) 879 } 880 881 // Delete range of keys 882 rangeStart := count / 2 883 rangeEnd := count * 3 / 4 884 batch.DeleteRange([]byte(strconv.Itoa(rangeStart)), []byte(strconv.Itoa(rangeEnd))) 885 886 // Write the batch 887 batch.Write() 888 } 889 890 b.Run("BatchMixedOps100", func(b *testing.B) { 891 benchBatchMixedOps(b, 100) 892 }) 893 b.Run("BatchMixedOps1k", func(b *testing.B) { 894 benchBatchMixedOps(b, 1000) 895 }) 896 b.Run("BatchMixedOps10k", func(b *testing.B) { 897 benchBatchMixedOps(b, 10000) 898 }) 899 }) 900 } 901 902 func iterateKeys(it ethdb.Iterator) []string { 903 keys := []string{} 904 for it.Next() { 905 keys = append(keys, string(it.Key())) 906 } 907 sort.Strings(keys) 908 it.Release() 909 return keys 910 } 911 912 // randBytes generates a random blob of data. 913 func randBytes(len int) []byte { 914 buf := make([]byte, len) 915 if n, err := rand.Read(buf); n != len || err != nil { 916 panic(err) 917 } 918 return buf 919 } 920 921 func makeDataset(size, ksize, vsize int, order bool) ([][]byte, [][]byte) { 922 var keys [][]byte 923 var vals [][]byte 924 for i := 0; i < size; i += 1 { 925 keys = append(keys, randBytes(ksize)) 926 vals = append(vals, randBytes(vsize)) 927 } 928 if order { 929 slices.SortFunc(keys, bytes.Compare) 930 } 931 return keys, vals 932 }