github.com/bhojpur/cache@v0.0.4/pkg/memory/bucket_test.go (about) 1 package memory_test 2 3 // Copyright (c) 2018 Bhojpur Consulting Private Limited, India. All rights reserved. 4 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 12 // The above copyright notice and this permission notice shall be included in 13 // all copies or substantial portions of the Software. 14 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 // THE SOFTWARE. 22 23 import ( 24 "bytes" 25 "encoding/binary" 26 "errors" 27 "fmt" 28 "log" 29 "math/rand" 30 "os" 31 "strconv" 32 "strings" 33 "testing" 34 "testing/quick" 35 36 memcache "github.com/bhojpur/cache/pkg/memory" 37 ) 38 39 // Ensure that a bucket that gets a non-existent key returns nil. 40 func TestBucket_Get_NonExistent(t *testing.T) { 41 db := MustOpenDB() 42 defer db.MustClose() 43 44 if err := db.Update(func(tx *memcache.Tx) error { 45 b, err := tx.CreateBucket([]byte("widgets")) 46 if err != nil { 47 t.Fatal(err) 48 } 49 if v := b.Get([]byte("foo")); v != nil { 50 t.Fatal("expected nil value") 51 } 52 return nil 53 }); err != nil { 54 t.Fatal(err) 55 } 56 } 57 58 // Ensure that a bucket can read a value that is not flushed yet. 59 func TestBucket_Get_FromNode(t *testing.T) { 60 db := MustOpenDB() 61 defer db.MustClose() 62 63 if err := db.Update(func(tx *memcache.Tx) error { 64 b, err := tx.CreateBucket([]byte("widgets")) 65 if err != nil { 66 t.Fatal(err) 67 } 68 if err := b.Put([]byte("foo"), []byte("bar")); err != nil { 69 t.Fatal(err) 70 } 71 if v := b.Get([]byte("foo")); !bytes.Equal(v, []byte("bar")) { 72 t.Fatalf("unexpected value: %v", v) 73 } 74 return nil 75 }); err != nil { 76 t.Fatal(err) 77 } 78 } 79 80 // Ensure that a bucket retrieved via Get() returns a nil. 81 func TestBucket_Get_IncompatibleValue(t *testing.T) { 82 db := MustOpenDB() 83 defer db.MustClose() 84 if err := db.Update(func(tx *memcache.Tx) error { 85 _, err := tx.CreateBucket([]byte("widgets")) 86 if err != nil { 87 t.Fatal(err) 88 } 89 90 if _, err := tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo")); err != nil { 91 t.Fatal(err) 92 } 93 94 if tx.Bucket([]byte("widgets")).Get([]byte("foo")) != nil { 95 t.Fatal("expected nil value") 96 } 97 return nil 98 }); err != nil { 99 t.Fatal(err) 100 } 101 } 102 103 // Ensure that a slice returned from a bucket has a capacity equal to its length. 104 // This also allows slices to be appended to since it will require a realloc by Go. 105 func TestBucket_Get_Capacity(t *testing.T) { 106 db := MustOpenDB() 107 defer db.MustClose() 108 109 // Write key to a bucket. 110 if err := db.Update(func(tx *memcache.Tx) error { 111 b, err := tx.CreateBucket([]byte("bucket")) 112 if err != nil { 113 return err 114 } 115 return b.Put([]byte("key"), []byte("val")) 116 }); err != nil { 117 t.Fatal(err) 118 } 119 120 // Retrieve value and attempt to append to it. 121 if err := db.Update(func(tx *memcache.Tx) error { 122 k, v := tx.Bucket([]byte("bucket")).Cursor().First() 123 124 // Verify capacity. 125 if len(k) != cap(k) { 126 t.Fatalf("unexpected key slice capacity: %d", cap(k)) 127 } else if len(v) != cap(v) { 128 t.Fatalf("unexpected value slice capacity: %d", cap(v)) 129 } 130 131 // Ensure slice can be appended to without a segfault. 132 k = append(k, []byte("123")...) 133 v = append(v, []byte("123")...) 134 135 return nil 136 }); err != nil { 137 t.Fatal(err) 138 } 139 } 140 141 // Ensure that a bucket can write a key/value. 142 func TestBucket_Put(t *testing.T) { 143 db := MustOpenDB() 144 defer db.MustClose() 145 if err := db.Update(func(tx *memcache.Tx) error { 146 b, err := tx.CreateBucket([]byte("widgets")) 147 if err != nil { 148 t.Fatal(err) 149 } 150 if err := b.Put([]byte("foo"), []byte("bar")); err != nil { 151 t.Fatal(err) 152 } 153 154 v := tx.Bucket([]byte("widgets")).Get([]byte("foo")) 155 if !bytes.Equal([]byte("bar"), v) { 156 t.Fatalf("unexpected value: %v", v) 157 } 158 return nil 159 }); err != nil { 160 t.Fatal(err) 161 } 162 } 163 164 // Ensure that a bucket can rewrite a key in the same transaction. 165 func TestBucket_Put_Repeat(t *testing.T) { 166 db := MustOpenDB() 167 defer db.MustClose() 168 if err := db.Update(func(tx *memcache.Tx) error { 169 b, err := tx.CreateBucket([]byte("widgets")) 170 if err != nil { 171 t.Fatal(err) 172 } 173 if err := b.Put([]byte("foo"), []byte("bar")); err != nil { 174 t.Fatal(err) 175 } 176 if err := b.Put([]byte("foo"), []byte("baz")); err != nil { 177 t.Fatal(err) 178 } 179 180 value := tx.Bucket([]byte("widgets")).Get([]byte("foo")) 181 if !bytes.Equal([]byte("baz"), value) { 182 t.Fatalf("unexpected value: %v", value) 183 } 184 return nil 185 }); err != nil { 186 t.Fatal(err) 187 } 188 } 189 190 // Ensure that a bucket can write a bunch of large values. 191 func TestBucket_Put_Large(t *testing.T) { 192 db := MustOpenDB() 193 defer db.MustClose() 194 195 count, factor := 100, 200 196 if err := db.Update(func(tx *memcache.Tx) error { 197 b, err := tx.CreateBucket([]byte("widgets")) 198 if err != nil { 199 t.Fatal(err) 200 } 201 for i := 1; i < count; i++ { 202 if err := b.Put([]byte(strings.Repeat("0", i*factor)), []byte(strings.Repeat("X", (count-i)*factor))); err != nil { 203 t.Fatal(err) 204 } 205 } 206 return nil 207 }); err != nil { 208 t.Fatal(err) 209 } 210 211 if err := db.View(func(tx *memcache.Tx) error { 212 b := tx.Bucket([]byte("widgets")) 213 for i := 1; i < count; i++ { 214 value := b.Get([]byte(strings.Repeat("0", i*factor))) 215 if !bytes.Equal(value, []byte(strings.Repeat("X", (count-i)*factor))) { 216 t.Fatalf("unexpected value: %v", value) 217 } 218 } 219 return nil 220 }); err != nil { 221 t.Fatal(err) 222 } 223 } 224 225 // Ensure that a database can perform multiple large appends safely. 226 func TestDB_Put_VeryLarge(t *testing.T) { 227 if testing.Short() { 228 t.Skip("skipping test in short mode.") 229 } 230 231 n, batchN := 400000, 200000 232 ksize, vsize := 8, 500 233 234 db := MustOpenDB() 235 defer db.MustClose() 236 237 for i := 0; i < n; i += batchN { 238 if err := db.Update(func(tx *memcache.Tx) error { 239 b, err := tx.CreateBucketIfNotExists([]byte("widgets")) 240 if err != nil { 241 t.Fatal(err) 242 } 243 for j := 0; j < batchN; j++ { 244 k, v := make([]byte, ksize), make([]byte, vsize) 245 binary.BigEndian.PutUint32(k, uint32(i+j)) 246 if err := b.Put(k, v); err != nil { 247 t.Fatal(err) 248 } 249 } 250 return nil 251 }); err != nil { 252 t.Fatal(err) 253 } 254 } 255 } 256 257 // Ensure that a setting a value on a key with a bucket value returns an error. 258 func TestBucket_Put_IncompatibleValue(t *testing.T) { 259 db := MustOpenDB() 260 defer db.MustClose() 261 262 if err := db.Update(func(tx *memcache.Tx) error { 263 b0, err := tx.CreateBucket([]byte("widgets")) 264 if err != nil { 265 t.Fatal(err) 266 } 267 268 if _, err := tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo")); err != nil { 269 t.Fatal(err) 270 } 271 if err := b0.Put([]byte("foo"), []byte("bar")); err != memcache.ErrIncompatibleValue { 272 t.Fatalf("unexpected error: %s", err) 273 } 274 return nil 275 }); err != nil { 276 t.Fatal(err) 277 } 278 } 279 280 // Ensure that a setting a value while the transaction is closed returns an error. 281 func TestBucket_Put_Closed(t *testing.T) { 282 db := MustOpenDB() 283 defer db.MustClose() 284 tx, err := db.Begin(true) 285 if err != nil { 286 t.Fatal(err) 287 } 288 289 b, err := tx.CreateBucket([]byte("widgets")) 290 if err != nil { 291 t.Fatal(err) 292 } 293 294 if err := tx.Rollback(); err != nil { 295 t.Fatal(err) 296 } 297 298 if err := b.Put([]byte("foo"), []byte("bar")); err != memcache.ErrTxClosed { 299 t.Fatalf("unexpected error: %s", err) 300 } 301 } 302 303 // Ensure that setting a value on a read-only bucket returns an error. 304 func TestBucket_Put_ReadOnly(t *testing.T) { 305 db := MustOpenDB() 306 defer db.MustClose() 307 308 if err := db.Update(func(tx *memcache.Tx) error { 309 if _, err := tx.CreateBucket([]byte("widgets")); err != nil { 310 t.Fatal(err) 311 } 312 return nil 313 }); err != nil { 314 t.Fatal(err) 315 } 316 317 if err := db.View(func(tx *memcache.Tx) error { 318 b := tx.Bucket([]byte("widgets")) 319 if err := b.Put([]byte("foo"), []byte("bar")); err != memcache.ErrTxNotWritable { 320 t.Fatalf("unexpected error: %s", err) 321 } 322 return nil 323 }); err != nil { 324 t.Fatal(err) 325 } 326 } 327 328 // Ensure that a bucket can delete an existing key. 329 func TestBucket_Delete(t *testing.T) { 330 db := MustOpenDB() 331 defer db.MustClose() 332 333 if err := db.Update(func(tx *memcache.Tx) error { 334 b, err := tx.CreateBucket([]byte("widgets")) 335 if err != nil { 336 t.Fatal(err) 337 } 338 if err := b.Put([]byte("foo"), []byte("bar")); err != nil { 339 t.Fatal(err) 340 } 341 if err := b.Delete([]byte("foo")); err != nil { 342 t.Fatal(err) 343 } 344 if v := b.Get([]byte("foo")); v != nil { 345 t.Fatalf("unexpected value: %v", v) 346 } 347 return nil 348 }); err != nil { 349 t.Fatal(err) 350 } 351 } 352 353 // Ensure that deleting a large set of keys will work correctly. 354 func TestBucket_Delete_Large(t *testing.T) { 355 db := MustOpenDB() 356 defer db.MustClose() 357 358 if err := db.Update(func(tx *memcache.Tx) error { 359 b, err := tx.CreateBucket([]byte("widgets")) 360 if err != nil { 361 t.Fatal(err) 362 } 363 364 for i := 0; i < 100; i++ { 365 if err := b.Put([]byte(strconv.Itoa(i)), []byte(strings.Repeat("*", 1024))); err != nil { 366 t.Fatal(err) 367 } 368 } 369 370 return nil 371 }); err != nil { 372 t.Fatal(err) 373 } 374 375 if err := db.Update(func(tx *memcache.Tx) error { 376 b := tx.Bucket([]byte("widgets")) 377 for i := 0; i < 100; i++ { 378 if err := b.Delete([]byte(strconv.Itoa(i))); err != nil { 379 t.Fatal(err) 380 } 381 } 382 return nil 383 }); err != nil { 384 t.Fatal(err) 385 } 386 387 if err := db.View(func(tx *memcache.Tx) error { 388 b := tx.Bucket([]byte("widgets")) 389 for i := 0; i < 100; i++ { 390 if v := b.Get([]byte(strconv.Itoa(i))); v != nil { 391 t.Fatalf("unexpected value: %v, i=%d", v, i) 392 } 393 } 394 return nil 395 }); err != nil { 396 t.Fatal(err) 397 } 398 } 399 400 // Deleting a very large list of keys will cause the freelist to use overflow. 401 func TestBucket_Delete_FreelistOverflow(t *testing.T) { 402 if testing.Short() { 403 t.Skip("skipping test in short mode.") 404 } 405 406 db := MustOpenDB() 407 defer db.MustClose() 408 409 k := make([]byte, 16) 410 for i := uint64(0); i < 10000; i++ { 411 if err := db.Update(func(tx *memcache.Tx) error { 412 b, err := tx.CreateBucketIfNotExists([]byte("0")) 413 if err != nil { 414 t.Fatalf("bucket error: %s", err) 415 } 416 417 for j := uint64(0); j < 1000; j++ { 418 binary.BigEndian.PutUint64(k[:8], i) 419 binary.BigEndian.PutUint64(k[8:], j) 420 if err := b.Put(k, nil); err != nil { 421 t.Fatalf("put error: %s", err) 422 } 423 } 424 425 return nil 426 }); err != nil { 427 t.Fatal(err) 428 } 429 } 430 431 // Delete all of them in one large transaction 432 if err := db.Update(func(tx *memcache.Tx) error { 433 b := tx.Bucket([]byte("0")) 434 c := b.Cursor() 435 for k, _ := c.First(); k != nil; k, _ = c.Next() { 436 if err := c.Delete(); err != nil { 437 t.Fatal(err) 438 } 439 } 440 return nil 441 }); err != nil { 442 t.Fatal(err) 443 } 444 } 445 446 // Ensure that accessing and updating nested buckets is ok across transactions. 447 func TestBucket_Nested(t *testing.T) { 448 db := MustOpenDB() 449 defer db.MustClose() 450 451 if err := db.Update(func(tx *memcache.Tx) error { 452 // Create a widgets bucket. 453 b, err := tx.CreateBucket([]byte("widgets")) 454 if err != nil { 455 t.Fatal(err) 456 } 457 458 // Create a widgets/foo bucket. 459 _, err = b.CreateBucket([]byte("foo")) 460 if err != nil { 461 t.Fatal(err) 462 } 463 464 // Create a widgets/bar key. 465 if err := b.Put([]byte("bar"), []byte("0000")); err != nil { 466 t.Fatal(err) 467 } 468 469 return nil 470 }); err != nil { 471 t.Fatal(err) 472 } 473 db.MustCheck() 474 475 // Update widgets/bar. 476 if err := db.Update(func(tx *memcache.Tx) error { 477 b := tx.Bucket([]byte("widgets")) 478 if err := b.Put([]byte("bar"), []byte("xxxx")); err != nil { 479 t.Fatal(err) 480 } 481 return nil 482 }); err != nil { 483 t.Fatal(err) 484 } 485 db.MustCheck() 486 487 // Cause a split. 488 if err := db.Update(func(tx *memcache.Tx) error { 489 var b = tx.Bucket([]byte("widgets")) 490 for i := 0; i < 10000; i++ { 491 if err := b.Put([]byte(strconv.Itoa(i)), []byte(strconv.Itoa(i))); err != nil { 492 t.Fatal(err) 493 } 494 } 495 return nil 496 }); err != nil { 497 t.Fatal(err) 498 } 499 db.MustCheck() 500 501 // Insert into widgets/foo/baz. 502 if err := db.Update(func(tx *memcache.Tx) error { 503 var b = tx.Bucket([]byte("widgets")) 504 if err := b.Bucket([]byte("foo")).Put([]byte("baz"), []byte("yyyy")); err != nil { 505 t.Fatal(err) 506 } 507 return nil 508 }); err != nil { 509 t.Fatal(err) 510 } 511 db.MustCheck() 512 513 // Verify. 514 if err := db.View(func(tx *memcache.Tx) error { 515 var b = tx.Bucket([]byte("widgets")) 516 if v := b.Bucket([]byte("foo")).Get([]byte("baz")); !bytes.Equal(v, []byte("yyyy")) { 517 t.Fatalf("unexpected value: %v", v) 518 } 519 if v := b.Get([]byte("bar")); !bytes.Equal(v, []byte("xxxx")) { 520 t.Fatalf("unexpected value: %v", v) 521 } 522 for i := 0; i < 10000; i++ { 523 if v := b.Get([]byte(strconv.Itoa(i))); !bytes.Equal(v, []byte(strconv.Itoa(i))) { 524 t.Fatalf("unexpected value: %v", v) 525 } 526 } 527 return nil 528 }); err != nil { 529 t.Fatal(err) 530 } 531 } 532 533 // Ensure that deleting a bucket using Delete() returns an error. 534 func TestBucket_Delete_Bucket(t *testing.T) { 535 db := MustOpenDB() 536 defer db.MustClose() 537 if err := db.Update(func(tx *memcache.Tx) error { 538 b, err := tx.CreateBucket([]byte("widgets")) 539 if err != nil { 540 t.Fatal(err) 541 } 542 if _, err := b.CreateBucket([]byte("foo")); err != nil { 543 t.Fatal(err) 544 } 545 if err := b.Delete([]byte("foo")); err != memcache.ErrIncompatibleValue { 546 t.Fatalf("unexpected error: %s", err) 547 } 548 return nil 549 }); err != nil { 550 t.Fatal(err) 551 } 552 } 553 554 // Ensure that deleting a key on a read-only bucket returns an error. 555 func TestBucket_Delete_ReadOnly(t *testing.T) { 556 db := MustOpenDB() 557 defer db.MustClose() 558 559 if err := db.Update(func(tx *memcache.Tx) error { 560 if _, err := tx.CreateBucket([]byte("widgets")); err != nil { 561 t.Fatal(err) 562 } 563 return nil 564 }); err != nil { 565 t.Fatal(err) 566 } 567 568 if err := db.View(func(tx *memcache.Tx) error { 569 if err := tx.Bucket([]byte("widgets")).Delete([]byte("foo")); err != memcache.ErrTxNotWritable { 570 t.Fatalf("unexpected error: %s", err) 571 } 572 return nil 573 }); err != nil { 574 t.Fatal(err) 575 } 576 } 577 578 // Ensure that a deleting value while the transaction is closed returns an error. 579 func TestBucket_Delete_Closed(t *testing.T) { 580 db := MustOpenDB() 581 defer db.MustClose() 582 583 tx, err := db.Begin(true) 584 if err != nil { 585 t.Fatal(err) 586 } 587 588 b, err := tx.CreateBucket([]byte("widgets")) 589 if err != nil { 590 t.Fatal(err) 591 } 592 593 if err := tx.Rollback(); err != nil { 594 t.Fatal(err) 595 } 596 if err := b.Delete([]byte("foo")); err != memcache.ErrTxClosed { 597 t.Fatalf("unexpected error: %s", err) 598 } 599 } 600 601 // Ensure that deleting a bucket causes nested buckets to be deleted. 602 func TestBucket_DeleteBucket_Nested(t *testing.T) { 603 db := MustOpenDB() 604 defer db.MustClose() 605 606 if err := db.Update(func(tx *memcache.Tx) error { 607 widgets, err := tx.CreateBucket([]byte("widgets")) 608 if err != nil { 609 t.Fatal(err) 610 } 611 612 foo, err := widgets.CreateBucket([]byte("foo")) 613 if err != nil { 614 t.Fatal(err) 615 } 616 617 bar, err := foo.CreateBucket([]byte("bar")) 618 if err != nil { 619 t.Fatal(err) 620 } 621 if err := bar.Put([]byte("baz"), []byte("bat")); err != nil { 622 t.Fatal(err) 623 } 624 if err := tx.Bucket([]byte("widgets")).DeleteBucket([]byte("foo")); err != nil { 625 t.Fatal(err) 626 } 627 return nil 628 }); err != nil { 629 t.Fatal(err) 630 } 631 } 632 633 // Ensure that deleting a bucket causes nested buckets to be deleted after they have been committed. 634 func TestBucket_DeleteBucket_Nested2(t *testing.T) { 635 db := MustOpenDB() 636 defer db.MustClose() 637 638 if err := db.Update(func(tx *memcache.Tx) error { 639 widgets, err := tx.CreateBucket([]byte("widgets")) 640 if err != nil { 641 t.Fatal(err) 642 } 643 644 foo, err := widgets.CreateBucket([]byte("foo")) 645 if err != nil { 646 t.Fatal(err) 647 } 648 649 bar, err := foo.CreateBucket([]byte("bar")) 650 if err != nil { 651 t.Fatal(err) 652 } 653 654 if err := bar.Put([]byte("baz"), []byte("bat")); err != nil { 655 t.Fatal(err) 656 } 657 return nil 658 }); err != nil { 659 t.Fatal(err) 660 } 661 662 if err := db.Update(func(tx *memcache.Tx) error { 663 widgets := tx.Bucket([]byte("widgets")) 664 if widgets == nil { 665 t.Fatal("expected widgets bucket") 666 } 667 668 foo := widgets.Bucket([]byte("foo")) 669 if foo == nil { 670 t.Fatal("expected foo bucket") 671 } 672 673 bar := foo.Bucket([]byte("bar")) 674 if bar == nil { 675 t.Fatal("expected bar bucket") 676 } 677 678 if v := bar.Get([]byte("baz")); !bytes.Equal(v, []byte("bat")) { 679 t.Fatalf("unexpected value: %v", v) 680 } 681 if err := tx.DeleteBucket([]byte("widgets")); err != nil { 682 t.Fatal(err) 683 } 684 return nil 685 }); err != nil { 686 t.Fatal(err) 687 } 688 689 if err := db.View(func(tx *memcache.Tx) error { 690 if tx.Bucket([]byte("widgets")) != nil { 691 t.Fatal("expected bucket to be deleted") 692 } 693 return nil 694 }); err != nil { 695 t.Fatal(err) 696 } 697 } 698 699 // Ensure that deleting a child bucket with multiple pages causes all pages to get collected. 700 // NOTE: Consistency check in memcache_test.DB.Close() will panic if pages not freed properly. 701 func TestBucket_DeleteBucket_Large(t *testing.T) { 702 db := MustOpenDB() 703 defer db.MustClose() 704 705 if err := db.Update(func(tx *memcache.Tx) error { 706 widgets, err := tx.CreateBucket([]byte("widgets")) 707 if err != nil { 708 t.Fatal(err) 709 } 710 711 foo, err := widgets.CreateBucket([]byte("foo")) 712 if err != nil { 713 t.Fatal(err) 714 } 715 716 for i := 0; i < 1000; i++ { 717 if err := foo.Put([]byte(fmt.Sprintf("%d", i)), []byte(fmt.Sprintf("%0100d", i))); err != nil { 718 t.Fatal(err) 719 } 720 } 721 return nil 722 }); err != nil { 723 t.Fatal(err) 724 } 725 726 if err := db.Update(func(tx *memcache.Tx) error { 727 if err := tx.DeleteBucket([]byte("widgets")); err != nil { 728 t.Fatal(err) 729 } 730 return nil 731 }); err != nil { 732 t.Fatal(err) 733 } 734 } 735 736 // Ensure that a simple value retrieved via Bucket() returns a nil. 737 func TestBucket_Bucket_IncompatibleValue(t *testing.T) { 738 db := MustOpenDB() 739 defer db.MustClose() 740 741 if err := db.Update(func(tx *memcache.Tx) error { 742 widgets, err := tx.CreateBucket([]byte("widgets")) 743 if err != nil { 744 t.Fatal(err) 745 } 746 747 if err := widgets.Put([]byte("foo"), []byte("bar")); err != nil { 748 t.Fatal(err) 749 } 750 if b := tx.Bucket([]byte("widgets")).Bucket([]byte("foo")); b != nil { 751 t.Fatal("expected nil bucket") 752 } 753 return nil 754 }); err != nil { 755 t.Fatal(err) 756 } 757 } 758 759 // Ensure that creating a bucket on an existing non-bucket key returns an error. 760 func TestBucket_CreateBucket_IncompatibleValue(t *testing.T) { 761 db := MustOpenDB() 762 defer db.MustClose() 763 if err := db.Update(func(tx *memcache.Tx) error { 764 widgets, err := tx.CreateBucket([]byte("widgets")) 765 if err != nil { 766 t.Fatal(err) 767 } 768 769 if err := widgets.Put([]byte("foo"), []byte("bar")); err != nil { 770 t.Fatal(err) 771 } 772 if _, err := widgets.CreateBucket([]byte("foo")); err != memcache.ErrIncompatibleValue { 773 t.Fatalf("unexpected error: %s", err) 774 } 775 return nil 776 }); err != nil { 777 t.Fatal(err) 778 } 779 } 780 781 // Ensure that deleting a bucket on an existing non-bucket key returns an error. 782 func TestBucket_DeleteBucket_IncompatibleValue(t *testing.T) { 783 db := MustOpenDB() 784 defer db.MustClose() 785 786 if err := db.Update(func(tx *memcache.Tx) error { 787 widgets, err := tx.CreateBucket([]byte("widgets")) 788 if err != nil { 789 t.Fatal(err) 790 } 791 if err := widgets.Put([]byte("foo"), []byte("bar")); err != nil { 792 t.Fatal(err) 793 } 794 if err := tx.Bucket([]byte("widgets")).DeleteBucket([]byte("foo")); err != memcache.ErrIncompatibleValue { 795 t.Fatalf("unexpected error: %s", err) 796 } 797 return nil 798 }); err != nil { 799 t.Fatal(err) 800 } 801 } 802 803 // Ensure bucket can set and update its sequence number. 804 func TestBucket_Sequence(t *testing.T) { 805 db := MustOpenDB() 806 defer db.MustClose() 807 808 if err := db.Update(func(tx *memcache.Tx) error { 809 bkt, err := tx.CreateBucket([]byte("0")) 810 if err != nil { 811 t.Fatal(err) 812 } 813 814 // Retrieve sequence. 815 if v := bkt.Sequence(); v != 0 { 816 t.Fatalf("unexpected sequence: %d", v) 817 } 818 819 // Update sequence. 820 if err := bkt.SetSequence(1000); err != nil { 821 t.Fatal(err) 822 } 823 824 // Read sequence again. 825 if v := bkt.Sequence(); v != 1000 { 826 t.Fatalf("unexpected sequence: %d", v) 827 } 828 829 return nil 830 }); err != nil { 831 t.Fatal(err) 832 } 833 834 // Verify sequence in separate transaction. 835 if err := db.View(func(tx *memcache.Tx) error { 836 if v := tx.Bucket([]byte("0")).Sequence(); v != 1000 { 837 t.Fatalf("unexpected sequence: %d", v) 838 } 839 return nil 840 }); err != nil { 841 t.Fatal(err) 842 } 843 } 844 845 // Ensure that a bucket can return an autoincrementing sequence. 846 func TestBucket_NextSequence(t *testing.T) { 847 db := MustOpenDB() 848 defer db.MustClose() 849 850 if err := db.Update(func(tx *memcache.Tx) error { 851 widgets, err := tx.CreateBucket([]byte("widgets")) 852 if err != nil { 853 t.Fatal(err) 854 } 855 woojits, err := tx.CreateBucket([]byte("woojits")) 856 if err != nil { 857 t.Fatal(err) 858 } 859 860 // Make sure sequence increments. 861 if seq, err := widgets.NextSequence(); err != nil { 862 t.Fatal(err) 863 } else if seq != 1 { 864 t.Fatalf("unexpecte sequence: %d", seq) 865 } 866 867 if seq, err := widgets.NextSequence(); err != nil { 868 t.Fatal(err) 869 } else if seq != 2 { 870 t.Fatalf("unexpected sequence: %d", seq) 871 } 872 873 // Buckets should be separate. 874 if seq, err := woojits.NextSequence(); err != nil { 875 t.Fatal(err) 876 } else if seq != 1 { 877 t.Fatalf("unexpected sequence: %d", 1) 878 } 879 880 return nil 881 }); err != nil { 882 t.Fatal(err) 883 } 884 } 885 886 // Ensure that a bucket will persist an autoincrementing sequence even if its 887 // the only thing updated on the bucket. 888 func TestBucket_NextSequence_Persist(t *testing.T) { 889 db := MustOpenDB() 890 defer db.MustClose() 891 892 if err := db.Update(func(tx *memcache.Tx) error { 893 if _, err := tx.CreateBucket([]byte("widgets")); err != nil { 894 t.Fatal(err) 895 } 896 return nil 897 }); err != nil { 898 t.Fatal(err) 899 } 900 901 if err := db.Update(func(tx *memcache.Tx) error { 902 if _, err := tx.Bucket([]byte("widgets")).NextSequence(); err != nil { 903 t.Fatal(err) 904 } 905 return nil 906 }); err != nil { 907 t.Fatal(err) 908 } 909 910 if err := db.Update(func(tx *memcache.Tx) error { 911 seq, err := tx.Bucket([]byte("widgets")).NextSequence() 912 if err != nil { 913 t.Fatalf("unexpected error: %s", err) 914 } else if seq != 2 { 915 t.Fatalf("unexpected sequence: %d", seq) 916 } 917 return nil 918 }); err != nil { 919 t.Fatal(err) 920 } 921 } 922 923 // Ensure that retrieving the next sequence on a read-only bucket returns an error. 924 func TestBucket_NextSequence_ReadOnly(t *testing.T) { 925 db := MustOpenDB() 926 defer db.MustClose() 927 928 if err := db.Update(func(tx *memcache.Tx) error { 929 if _, err := tx.CreateBucket([]byte("widgets")); err != nil { 930 t.Fatal(err) 931 } 932 return nil 933 }); err != nil { 934 t.Fatal(err) 935 } 936 937 if err := db.View(func(tx *memcache.Tx) error { 938 _, err := tx.Bucket([]byte("widgets")).NextSequence() 939 if err != memcache.ErrTxNotWritable { 940 t.Fatalf("unexpected error: %s", err) 941 } 942 return nil 943 }); err != nil { 944 t.Fatal(err) 945 } 946 } 947 948 // Ensure that retrieving the next sequence for a bucket on a closed database return an error. 949 func TestBucket_NextSequence_Closed(t *testing.T) { 950 db := MustOpenDB() 951 defer db.MustClose() 952 tx, err := db.Begin(true) 953 if err != nil { 954 t.Fatal(err) 955 } 956 b, err := tx.CreateBucket([]byte("widgets")) 957 if err != nil { 958 t.Fatal(err) 959 } 960 if err := tx.Rollback(); err != nil { 961 t.Fatal(err) 962 } 963 if _, err := b.NextSequence(); err != memcache.ErrTxClosed { 964 t.Fatal(err) 965 } 966 } 967 968 // Ensure a user can loop over all key/value pairs in a bucket. 969 func TestBucket_ForEach(t *testing.T) { 970 db := MustOpenDB() 971 defer db.MustClose() 972 973 if err := db.Update(func(tx *memcache.Tx) error { 974 b, err := tx.CreateBucket([]byte("widgets")) 975 if err != nil { 976 t.Fatal(err) 977 } 978 if err := b.Put([]byte("foo"), []byte("0000")); err != nil { 979 t.Fatal(err) 980 } 981 if err := b.Put([]byte("baz"), []byte("0001")); err != nil { 982 t.Fatal(err) 983 } 984 if err := b.Put([]byte("bar"), []byte("0002")); err != nil { 985 t.Fatal(err) 986 } 987 988 var index int 989 if err := b.ForEach(func(k, v []byte) error { 990 switch index { 991 case 0: 992 if !bytes.Equal(k, []byte("bar")) { 993 t.Fatalf("unexpected key: %v", k) 994 } else if !bytes.Equal(v, []byte("0002")) { 995 t.Fatalf("unexpected value: %v", v) 996 } 997 case 1: 998 if !bytes.Equal(k, []byte("baz")) { 999 t.Fatalf("unexpected key: %v", k) 1000 } else if !bytes.Equal(v, []byte("0001")) { 1001 t.Fatalf("unexpected value: %v", v) 1002 } 1003 case 2: 1004 if !bytes.Equal(k, []byte("foo")) { 1005 t.Fatalf("unexpected key: %v", k) 1006 } else if !bytes.Equal(v, []byte("0000")) { 1007 t.Fatalf("unexpected value: %v", v) 1008 } 1009 } 1010 index++ 1011 return nil 1012 }); err != nil { 1013 t.Fatal(err) 1014 } 1015 1016 if index != 3 { 1017 t.Fatalf("unexpected index: %d", index) 1018 } 1019 1020 return nil 1021 }); err != nil { 1022 t.Fatal(err) 1023 } 1024 } 1025 1026 // Ensure a database can stop iteration early. 1027 func TestBucket_ForEach_ShortCircuit(t *testing.T) { 1028 db := MustOpenDB() 1029 defer db.MustClose() 1030 if err := db.Update(func(tx *memcache.Tx) error { 1031 b, err := tx.CreateBucket([]byte("widgets")) 1032 if err != nil { 1033 t.Fatal(err) 1034 } 1035 if err := b.Put([]byte("bar"), []byte("0000")); err != nil { 1036 t.Fatal(err) 1037 } 1038 if err := b.Put([]byte("baz"), []byte("0000")); err != nil { 1039 t.Fatal(err) 1040 } 1041 if err := b.Put([]byte("foo"), []byte("0000")); err != nil { 1042 t.Fatal(err) 1043 } 1044 1045 var index int 1046 if err := tx.Bucket([]byte("widgets")).ForEach(func(k, v []byte) error { 1047 index++ 1048 if bytes.Equal(k, []byte("baz")) { 1049 return errors.New("marker") 1050 } 1051 return nil 1052 }); err == nil || err.Error() != "marker" { 1053 t.Fatalf("unexpected error: %s", err) 1054 } 1055 if index != 2 { 1056 t.Fatalf("unexpected index: %d", index) 1057 } 1058 1059 return nil 1060 }); err != nil { 1061 t.Fatal(err) 1062 } 1063 } 1064 1065 // Ensure that looping over a bucket on a closed database returns an error. 1066 func TestBucket_ForEach_Closed(t *testing.T) { 1067 db := MustOpenDB() 1068 defer db.MustClose() 1069 1070 tx, err := db.Begin(true) 1071 if err != nil { 1072 t.Fatal(err) 1073 } 1074 1075 b, err := tx.CreateBucket([]byte("widgets")) 1076 if err != nil { 1077 t.Fatal(err) 1078 } 1079 1080 if err := tx.Rollback(); err != nil { 1081 t.Fatal(err) 1082 } 1083 1084 if err := b.ForEach(func(k, v []byte) error { return nil }); err != memcache.ErrTxClosed { 1085 t.Fatalf("unexpected error: %s", err) 1086 } 1087 } 1088 1089 // Ensure that an error is returned when inserting with an empty key. 1090 func TestBucket_Put_EmptyKey(t *testing.T) { 1091 db := MustOpenDB() 1092 defer db.MustClose() 1093 1094 if err := db.Update(func(tx *memcache.Tx) error { 1095 b, err := tx.CreateBucket([]byte("widgets")) 1096 if err != nil { 1097 t.Fatal(err) 1098 } 1099 if err := b.Put([]byte(""), []byte("bar")); err != memcache.ErrKeyRequired { 1100 t.Fatalf("unexpected error: %s", err) 1101 } 1102 if err := b.Put(nil, []byte("bar")); err != memcache.ErrKeyRequired { 1103 t.Fatalf("unexpected error: %s", err) 1104 } 1105 return nil 1106 }); err != nil { 1107 t.Fatal(err) 1108 } 1109 } 1110 1111 // Ensure that an error is returned when inserting with a key that's too large. 1112 func TestBucket_Put_KeyTooLarge(t *testing.T) { 1113 db := MustOpenDB() 1114 defer db.MustClose() 1115 if err := db.Update(func(tx *memcache.Tx) error { 1116 b, err := tx.CreateBucket([]byte("widgets")) 1117 if err != nil { 1118 t.Fatal(err) 1119 } 1120 if err := b.Put(make([]byte, 32769), []byte("bar")); err != memcache.ErrKeyTooLarge { 1121 t.Fatalf("unexpected error: %s", err) 1122 } 1123 return nil 1124 }); err != nil { 1125 t.Fatal(err) 1126 } 1127 } 1128 1129 // Ensure that an error is returned when inserting a value that's too large. 1130 func TestBucket_Put_ValueTooLarge(t *testing.T) { 1131 // Skip this test on DroneCI because the machine is resource constrained. 1132 if os.Getenv("DRONE") == "true" { 1133 t.Skip("not enough RAM for test") 1134 } 1135 1136 db := MustOpenDB() 1137 defer db.MustClose() 1138 1139 if err := db.Update(func(tx *memcache.Tx) error { 1140 b, err := tx.CreateBucket([]byte("widgets")) 1141 if err != nil { 1142 t.Fatal(err) 1143 } 1144 if err := b.Put([]byte("foo"), make([]byte, memcache.MaxValueSize+1)); err != memcache.ErrValueTooLarge { 1145 t.Fatalf("unexpected error: %s", err) 1146 } 1147 return nil 1148 }); err != nil { 1149 t.Fatal(err) 1150 } 1151 } 1152 1153 // Ensure a bucket can calculate stats. 1154 func TestBucket_Stats(t *testing.T) { 1155 db := MustOpenDB() 1156 defer db.MustClose() 1157 1158 // Add bucket with fewer keys but one big value. 1159 bigKey := []byte("really-big-value") 1160 for i := 0; i < 500; i++ { 1161 if err := db.Update(func(tx *memcache.Tx) error { 1162 b, err := tx.CreateBucketIfNotExists([]byte("woojits")) 1163 if err != nil { 1164 t.Fatal(err) 1165 } 1166 1167 if err := b.Put([]byte(fmt.Sprintf("%03d", i)), []byte(strconv.Itoa(i))); err != nil { 1168 t.Fatal(err) 1169 } 1170 return nil 1171 }); err != nil { 1172 t.Fatal(err) 1173 } 1174 } 1175 if err := db.Update(func(tx *memcache.Tx) error { 1176 if err := tx.Bucket([]byte("woojits")).Put(bigKey, []byte(strings.Repeat("*", 10000))); err != nil { 1177 t.Fatal(err) 1178 } 1179 return nil 1180 }); err != nil { 1181 t.Fatal(err) 1182 } 1183 1184 db.MustCheck() 1185 1186 if err := db.View(func(tx *memcache.Tx) error { 1187 stats := tx.Bucket([]byte("woojits")).Stats() 1188 if stats.BranchPageN != 1 { 1189 t.Fatalf("unexpected BranchPageN: %d", stats.BranchPageN) 1190 } else if stats.BranchOverflowN != 0 { 1191 t.Fatalf("unexpected BranchOverflowN: %d", stats.BranchOverflowN) 1192 } else if stats.LeafPageN != 7 { 1193 t.Fatalf("unexpected LeafPageN: %d", stats.LeafPageN) 1194 } else if stats.LeafOverflowN != 2 { 1195 t.Fatalf("unexpected LeafOverflowN: %d", stats.LeafOverflowN) 1196 } else if stats.KeyN != 501 { 1197 t.Fatalf("unexpected KeyN: %d", stats.KeyN) 1198 } else if stats.Depth != 2 { 1199 t.Fatalf("unexpected Depth: %d", stats.Depth) 1200 } 1201 1202 branchInuse := 16 // branch page header 1203 branchInuse += 7 * 16 // branch elements 1204 branchInuse += 7 * 3 // branch keys (6 3-byte keys) 1205 if stats.BranchInuse != branchInuse { 1206 t.Fatalf("unexpected BranchInuse: %d", stats.BranchInuse) 1207 } 1208 1209 leafInuse := 7 * 16 // leaf page header 1210 leafInuse += 501 * 16 // leaf elements 1211 leafInuse += 500*3 + len(bigKey) // leaf keys 1212 leafInuse += 1*10 + 2*90 + 3*400 + 10000 // leaf values 1213 if stats.LeafInuse != leafInuse { 1214 t.Fatalf("unexpected LeafInuse: %d", stats.LeafInuse) 1215 } 1216 1217 // Only check allocations for 4KB pages. 1218 if os.Getpagesize() == 4096 { 1219 if stats.BranchAlloc != 4096 { 1220 t.Fatalf("unexpected BranchAlloc: %d", stats.BranchAlloc) 1221 } else if stats.LeafAlloc != 36864 { 1222 t.Fatalf("unexpected LeafAlloc: %d", stats.LeafAlloc) 1223 } 1224 } 1225 1226 if stats.BucketN != 1 { 1227 t.Fatalf("unexpected BucketN: %d", stats.BucketN) 1228 } else if stats.InlineBucketN != 0 { 1229 t.Fatalf("unexpected InlineBucketN: %d", stats.InlineBucketN) 1230 } else if stats.InlineBucketInuse != 0 { 1231 t.Fatalf("unexpected InlineBucketInuse: %d", stats.InlineBucketInuse) 1232 } 1233 1234 return nil 1235 }); err != nil { 1236 t.Fatal(err) 1237 } 1238 } 1239 1240 // Ensure a bucket with random insertion utilizes fill percentage correctly. 1241 func TestBucket_Stats_RandomFill(t *testing.T) { 1242 if testing.Short() { 1243 t.Skip("skipping test in short mode.") 1244 } else if os.Getpagesize() != 4096 { 1245 t.Skip("invalid page size for test") 1246 } 1247 1248 db := MustOpenDB() 1249 defer db.MustClose() 1250 1251 // Add a set of values in random order. It will be the same random 1252 // order so we can maintain consistency between test runs. 1253 var count int 1254 rand := rand.New(rand.NewSource(42)) 1255 for _, i := range rand.Perm(1000) { 1256 if err := db.Update(func(tx *memcache.Tx) error { 1257 b, err := tx.CreateBucketIfNotExists([]byte("woojits")) 1258 if err != nil { 1259 t.Fatal(err) 1260 } 1261 b.FillPercent = 0.9 1262 for _, j := range rand.Perm(100) { 1263 index := (j * 10000) + i 1264 if err := b.Put([]byte(fmt.Sprintf("%d000000000000000", index)), []byte("0000000000")); err != nil { 1265 t.Fatal(err) 1266 } 1267 count++ 1268 } 1269 return nil 1270 }); err != nil { 1271 t.Fatal(err) 1272 } 1273 } 1274 1275 db.MustCheck() 1276 1277 if err := db.View(func(tx *memcache.Tx) error { 1278 stats := tx.Bucket([]byte("woojits")).Stats() 1279 if stats.KeyN != 100000 { 1280 t.Fatalf("unexpected KeyN: %d", stats.KeyN) 1281 } 1282 1283 if stats.BranchPageN != 98 { 1284 t.Fatalf("unexpected BranchPageN: %d", stats.BranchPageN) 1285 } else if stats.BranchOverflowN != 0 { 1286 t.Fatalf("unexpected BranchOverflowN: %d", stats.BranchOverflowN) 1287 } else if stats.BranchInuse != 130984 { 1288 t.Fatalf("unexpected BranchInuse: %d", stats.BranchInuse) 1289 } else if stats.BranchAlloc != 401408 { 1290 t.Fatalf("unexpected BranchAlloc: %d", stats.BranchAlloc) 1291 } 1292 1293 if stats.LeafPageN != 3412 { 1294 t.Fatalf("unexpected LeafPageN: %d", stats.LeafPageN) 1295 } else if stats.LeafOverflowN != 0 { 1296 t.Fatalf("unexpected LeafOverflowN: %d", stats.LeafOverflowN) 1297 } else if stats.LeafInuse != 4742482 { 1298 t.Fatalf("unexpected LeafInuse: %d", stats.LeafInuse) 1299 } else if stats.LeafAlloc != 13975552 { 1300 t.Fatalf("unexpected LeafAlloc: %d", stats.LeafAlloc) 1301 } 1302 return nil 1303 }); err != nil { 1304 t.Fatal(err) 1305 } 1306 } 1307 1308 // Ensure a bucket can calculate stats. 1309 func TestBucket_Stats_Small(t *testing.T) { 1310 db := MustOpenDB() 1311 defer db.MustClose() 1312 1313 if err := db.Update(func(tx *memcache.Tx) error { 1314 // Add a bucket that fits on a single root leaf. 1315 b, err := tx.CreateBucket([]byte("whozawhats")) 1316 if err != nil { 1317 t.Fatal(err) 1318 } 1319 if err := b.Put([]byte("foo"), []byte("bar")); err != nil { 1320 t.Fatal(err) 1321 } 1322 1323 return nil 1324 }); err != nil { 1325 t.Fatal(err) 1326 } 1327 1328 db.MustCheck() 1329 1330 if err := db.View(func(tx *memcache.Tx) error { 1331 b := tx.Bucket([]byte("whozawhats")) 1332 stats := b.Stats() 1333 if stats.BranchPageN != 0 { 1334 t.Fatalf("unexpected BranchPageN: %d", stats.BranchPageN) 1335 } else if stats.BranchOverflowN != 0 { 1336 t.Fatalf("unexpected BranchOverflowN: %d", stats.BranchOverflowN) 1337 } else if stats.LeafPageN != 0 { 1338 t.Fatalf("unexpected LeafPageN: %d", stats.LeafPageN) 1339 } else if stats.LeafOverflowN != 0 { 1340 t.Fatalf("unexpected LeafOverflowN: %d", stats.LeafOverflowN) 1341 } else if stats.KeyN != 1 { 1342 t.Fatalf("unexpected KeyN: %d", stats.KeyN) 1343 } else if stats.Depth != 1 { 1344 t.Fatalf("unexpected Depth: %d", stats.Depth) 1345 } else if stats.BranchInuse != 0 { 1346 t.Fatalf("unexpected BranchInuse: %d", stats.BranchInuse) 1347 } else if stats.LeafInuse != 0 { 1348 t.Fatalf("unexpected LeafInuse: %d", stats.LeafInuse) 1349 } 1350 1351 if os.Getpagesize() == 4096 { 1352 if stats.BranchAlloc != 0 { 1353 t.Fatalf("unexpected BranchAlloc: %d", stats.BranchAlloc) 1354 } else if stats.LeafAlloc != 0 { 1355 t.Fatalf("unexpected LeafAlloc: %d", stats.LeafAlloc) 1356 } 1357 } 1358 1359 if stats.BucketN != 1 { 1360 t.Fatalf("unexpected BucketN: %d", stats.BucketN) 1361 } else if stats.InlineBucketN != 1 { 1362 t.Fatalf("unexpected InlineBucketN: %d", stats.InlineBucketN) 1363 } else if stats.InlineBucketInuse != 16+16+6 { 1364 t.Fatalf("unexpected InlineBucketInuse: %d", stats.InlineBucketInuse) 1365 } 1366 1367 return nil 1368 }); err != nil { 1369 t.Fatal(err) 1370 } 1371 } 1372 1373 func TestBucket_Stats_EmptyBucket(t *testing.T) { 1374 db := MustOpenDB() 1375 defer db.MustClose() 1376 1377 if err := db.Update(func(tx *memcache.Tx) error { 1378 // Add a bucket that fits on a single root leaf. 1379 if _, err := tx.CreateBucket([]byte("whozawhats")); err != nil { 1380 t.Fatal(err) 1381 } 1382 return nil 1383 }); err != nil { 1384 t.Fatal(err) 1385 } 1386 1387 db.MustCheck() 1388 1389 if err := db.View(func(tx *memcache.Tx) error { 1390 b := tx.Bucket([]byte("whozawhats")) 1391 stats := b.Stats() 1392 if stats.BranchPageN != 0 { 1393 t.Fatalf("unexpected BranchPageN: %d", stats.BranchPageN) 1394 } else if stats.BranchOverflowN != 0 { 1395 t.Fatalf("unexpected BranchOverflowN: %d", stats.BranchOverflowN) 1396 } else if stats.LeafPageN != 0 { 1397 t.Fatalf("unexpected LeafPageN: %d", stats.LeafPageN) 1398 } else if stats.LeafOverflowN != 0 { 1399 t.Fatalf("unexpected LeafOverflowN: %d", stats.LeafOverflowN) 1400 } else if stats.KeyN != 0 { 1401 t.Fatalf("unexpected KeyN: %d", stats.KeyN) 1402 } else if stats.Depth != 1 { 1403 t.Fatalf("unexpected Depth: %d", stats.Depth) 1404 } else if stats.BranchInuse != 0 { 1405 t.Fatalf("unexpected BranchInuse: %d", stats.BranchInuse) 1406 } else if stats.LeafInuse != 0 { 1407 t.Fatalf("unexpected LeafInuse: %d", stats.LeafInuse) 1408 } 1409 1410 if os.Getpagesize() == 4096 { 1411 if stats.BranchAlloc != 0 { 1412 t.Fatalf("unexpected BranchAlloc: %d", stats.BranchAlloc) 1413 } else if stats.LeafAlloc != 0 { 1414 t.Fatalf("unexpected LeafAlloc: %d", stats.LeafAlloc) 1415 } 1416 } 1417 1418 if stats.BucketN != 1 { 1419 t.Fatalf("unexpected BucketN: %d", stats.BucketN) 1420 } else if stats.InlineBucketN != 1 { 1421 t.Fatalf("unexpected InlineBucketN: %d", stats.InlineBucketN) 1422 } else if stats.InlineBucketInuse != 16 { 1423 t.Fatalf("unexpected InlineBucketInuse: %d", stats.InlineBucketInuse) 1424 } 1425 1426 return nil 1427 }); err != nil { 1428 t.Fatal(err) 1429 } 1430 } 1431 1432 // Ensure a bucket can calculate stats. 1433 func TestBucket_Stats_Nested(t *testing.T) { 1434 db := MustOpenDB() 1435 defer db.MustClose() 1436 1437 if err := db.Update(func(tx *memcache.Tx) error { 1438 b, err := tx.CreateBucket([]byte("foo")) 1439 if err != nil { 1440 t.Fatal(err) 1441 } 1442 for i := 0; i < 100; i++ { 1443 if err := b.Put([]byte(fmt.Sprintf("%02d", i)), []byte(fmt.Sprintf("%02d", i))); err != nil { 1444 t.Fatal(err) 1445 } 1446 } 1447 1448 bar, err := b.CreateBucket([]byte("bar")) 1449 if err != nil { 1450 t.Fatal(err) 1451 } 1452 for i := 0; i < 10; i++ { 1453 if err := bar.Put([]byte(strconv.Itoa(i)), []byte(strconv.Itoa(i))); err != nil { 1454 t.Fatal(err) 1455 } 1456 } 1457 1458 baz, err := bar.CreateBucket([]byte("baz")) 1459 if err != nil { 1460 t.Fatal(err) 1461 } 1462 for i := 0; i < 10; i++ { 1463 if err := baz.Put([]byte(strconv.Itoa(i)), []byte(strconv.Itoa(i))); err != nil { 1464 t.Fatal(err) 1465 } 1466 } 1467 1468 return nil 1469 }); err != nil { 1470 t.Fatal(err) 1471 } 1472 1473 db.MustCheck() 1474 1475 if err := db.View(func(tx *memcache.Tx) error { 1476 b := tx.Bucket([]byte("foo")) 1477 stats := b.Stats() 1478 if stats.BranchPageN != 0 { 1479 t.Fatalf("unexpected BranchPageN: %d", stats.BranchPageN) 1480 } else if stats.BranchOverflowN != 0 { 1481 t.Fatalf("unexpected BranchOverflowN: %d", stats.BranchOverflowN) 1482 } else if stats.LeafPageN != 2 { 1483 t.Fatalf("unexpected LeafPageN: %d", stats.LeafPageN) 1484 } else if stats.LeafOverflowN != 0 { 1485 t.Fatalf("unexpected LeafOverflowN: %d", stats.LeafOverflowN) 1486 } else if stats.KeyN != 122 { 1487 t.Fatalf("unexpected KeyN: %d", stats.KeyN) 1488 } else if stats.Depth != 3 { 1489 t.Fatalf("unexpected Depth: %d", stats.Depth) 1490 } else if stats.BranchInuse != 0 { 1491 t.Fatalf("unexpected BranchInuse: %d", stats.BranchInuse) 1492 } 1493 1494 foo := 16 // foo (pghdr) 1495 foo += 101 * 16 // foo leaf elements 1496 foo += 100*2 + 100*2 // foo leaf key/values 1497 foo += 3 + 16 // foo -> bar key/value 1498 1499 bar := 16 // bar (pghdr) 1500 bar += 11 * 16 // bar leaf elements 1501 bar += 10 + 10 // bar leaf key/values 1502 bar += 3 + 16 // bar -> baz key/value 1503 1504 baz := 16 // baz (inline) (pghdr) 1505 baz += 10 * 16 // baz leaf elements 1506 baz += 10 + 10 // baz leaf key/values 1507 1508 if stats.LeafInuse != foo+bar+baz { 1509 t.Fatalf("unexpected LeafInuse: %d", stats.LeafInuse) 1510 } 1511 1512 if os.Getpagesize() == 4096 { 1513 if stats.BranchAlloc != 0 { 1514 t.Fatalf("unexpected BranchAlloc: %d", stats.BranchAlloc) 1515 } else if stats.LeafAlloc != 8192 { 1516 t.Fatalf("unexpected LeafAlloc: %d", stats.LeafAlloc) 1517 } 1518 } 1519 1520 if stats.BucketN != 3 { 1521 t.Fatalf("unexpected BucketN: %d", stats.BucketN) 1522 } else if stats.InlineBucketN != 1 { 1523 t.Fatalf("unexpected InlineBucketN: %d", stats.InlineBucketN) 1524 } else if stats.InlineBucketInuse != baz { 1525 t.Fatalf("unexpected InlineBucketInuse: %d", stats.InlineBucketInuse) 1526 } 1527 1528 return nil 1529 }); err != nil { 1530 t.Fatal(err) 1531 } 1532 } 1533 1534 // Ensure a large bucket can calculate stats. 1535 func TestBucket_Stats_Large(t *testing.T) { 1536 if testing.Short() { 1537 t.Skip("skipping test in short mode.") 1538 } 1539 1540 db := MustOpenDB() 1541 defer db.MustClose() 1542 1543 var index int 1544 for i := 0; i < 100; i++ { 1545 // Add bucket with lots of keys. 1546 if err := db.Update(func(tx *memcache.Tx) error { 1547 b, err := tx.CreateBucketIfNotExists([]byte("widgets")) 1548 if err != nil { 1549 t.Fatal(err) 1550 } 1551 for i := 0; i < 1000; i++ { 1552 if err := b.Put([]byte(strconv.Itoa(index)), []byte(strconv.Itoa(index))); err != nil { 1553 t.Fatal(err) 1554 } 1555 index++ 1556 } 1557 return nil 1558 }); err != nil { 1559 t.Fatal(err) 1560 } 1561 } 1562 1563 db.MustCheck() 1564 1565 if err := db.View(func(tx *memcache.Tx) error { 1566 stats := tx.Bucket([]byte("widgets")).Stats() 1567 if stats.BranchPageN != 13 { 1568 t.Fatalf("unexpected BranchPageN: %d", stats.BranchPageN) 1569 } else if stats.BranchOverflowN != 0 { 1570 t.Fatalf("unexpected BranchOverflowN: %d", stats.BranchOverflowN) 1571 } else if stats.LeafPageN != 1196 { 1572 t.Fatalf("unexpected LeafPageN: %d", stats.LeafPageN) 1573 } else if stats.LeafOverflowN != 0 { 1574 t.Fatalf("unexpected LeafOverflowN: %d", stats.LeafOverflowN) 1575 } else if stats.KeyN != 100000 { 1576 t.Fatalf("unexpected KeyN: %d", stats.KeyN) 1577 } else if stats.Depth != 3 { 1578 t.Fatalf("unexpected Depth: %d", stats.Depth) 1579 } else if stats.BranchInuse != 25257 { 1580 t.Fatalf("unexpected BranchInuse: %d", stats.BranchInuse) 1581 } else if stats.LeafInuse != 2596916 { 1582 t.Fatalf("unexpected LeafInuse: %d", stats.LeafInuse) 1583 } 1584 1585 if os.Getpagesize() == 4096 { 1586 if stats.BranchAlloc != 53248 { 1587 t.Fatalf("unexpected BranchAlloc: %d", stats.BranchAlloc) 1588 } else if stats.LeafAlloc != 4898816 { 1589 t.Fatalf("unexpected LeafAlloc: %d", stats.LeafAlloc) 1590 } 1591 } 1592 1593 if stats.BucketN != 1 { 1594 t.Fatalf("unexpected BucketN: %d", stats.BucketN) 1595 } else if stats.InlineBucketN != 0 { 1596 t.Fatalf("unexpected InlineBucketN: %d", stats.InlineBucketN) 1597 } else if stats.InlineBucketInuse != 0 { 1598 t.Fatalf("unexpected InlineBucketInuse: %d", stats.InlineBucketInuse) 1599 } 1600 1601 return nil 1602 }); err != nil { 1603 t.Fatal(err) 1604 } 1605 } 1606 1607 // Ensure that a bucket can write random keys and values across multiple transactions. 1608 func TestBucket_Put_Single(t *testing.T) { 1609 if testing.Short() { 1610 t.Skip("skipping test in short mode.") 1611 } 1612 1613 index := 0 1614 if err := quick.Check(func(items testdata) bool { 1615 db := MustOpenDB() 1616 defer db.MustClose() 1617 1618 m := make(map[string][]byte) 1619 1620 if err := db.Update(func(tx *memcache.Tx) error { 1621 if _, err := tx.CreateBucket([]byte("widgets")); err != nil { 1622 t.Fatal(err) 1623 } 1624 return nil 1625 }); err != nil { 1626 t.Fatal(err) 1627 } 1628 1629 for _, item := range items { 1630 if err := db.Update(func(tx *memcache.Tx) error { 1631 if err := tx.Bucket([]byte("widgets")).Put(item.Key, item.Value); err != nil { 1632 panic("put error: " + err.Error()) 1633 } 1634 m[string(item.Key)] = item.Value 1635 return nil 1636 }); err != nil { 1637 t.Fatal(err) 1638 } 1639 1640 // Verify all key/values so far. 1641 if err := db.View(func(tx *memcache.Tx) error { 1642 i := 0 1643 for k, v := range m { 1644 value := tx.Bucket([]byte("widgets")).Get([]byte(k)) 1645 if !bytes.Equal(value, v) { 1646 t.Logf("value mismatch [run %d] (%d of %d):\nkey: %x\ngot: %x\nexp: %x", index, i, len(m), []byte(k), value, v) 1647 db.CopyTempFile() 1648 t.FailNow() 1649 } 1650 i++ 1651 } 1652 return nil 1653 }); err != nil { 1654 t.Fatal(err) 1655 } 1656 } 1657 1658 index++ 1659 return true 1660 }, nil); err != nil { 1661 t.Error(err) 1662 } 1663 } 1664 1665 // Ensure that a transaction can insert multiple key/value pairs at once. 1666 func TestBucket_Put_Multiple(t *testing.T) { 1667 if testing.Short() { 1668 t.Skip("skipping test in short mode.") 1669 } 1670 1671 if err := quick.Check(func(items testdata) bool { 1672 db := MustOpenDB() 1673 defer db.MustClose() 1674 1675 // Bulk insert all values. 1676 if err := db.Update(func(tx *memcache.Tx) error { 1677 if _, err := tx.CreateBucket([]byte("widgets")); err != nil { 1678 t.Fatal(err) 1679 } 1680 return nil 1681 }); err != nil { 1682 t.Fatal(err) 1683 } 1684 1685 if err := db.Update(func(tx *memcache.Tx) error { 1686 b := tx.Bucket([]byte("widgets")) 1687 for _, item := range items { 1688 if err := b.Put(item.Key, item.Value); err != nil { 1689 t.Fatal(err) 1690 } 1691 } 1692 return nil 1693 }); err != nil { 1694 t.Fatal(err) 1695 } 1696 1697 // Verify all items exist. 1698 if err := db.View(func(tx *memcache.Tx) error { 1699 b := tx.Bucket([]byte("widgets")) 1700 for _, item := range items { 1701 value := b.Get(item.Key) 1702 if !bytes.Equal(item.Value, value) { 1703 db.CopyTempFile() 1704 t.Fatalf("exp=%x; got=%x", item.Value, value) 1705 } 1706 } 1707 return nil 1708 }); err != nil { 1709 t.Fatal(err) 1710 } 1711 1712 return true 1713 }, qconfig()); err != nil { 1714 t.Error(err) 1715 } 1716 } 1717 1718 // Ensure that a transaction can delete all key/value pairs and return to a single leaf page. 1719 func TestBucket_Delete_Quick(t *testing.T) { 1720 if testing.Short() { 1721 t.Skip("skipping test in short mode.") 1722 } 1723 1724 if err := quick.Check(func(items testdata) bool { 1725 db := MustOpenDB() 1726 defer db.MustClose() 1727 1728 // Bulk insert all values. 1729 if err := db.Update(func(tx *memcache.Tx) error { 1730 if _, err := tx.CreateBucket([]byte("widgets")); err != nil { 1731 t.Fatal(err) 1732 } 1733 return nil 1734 }); err != nil { 1735 t.Fatal(err) 1736 } 1737 1738 if err := db.Update(func(tx *memcache.Tx) error { 1739 b := tx.Bucket([]byte("widgets")) 1740 for _, item := range items { 1741 if err := b.Put(item.Key, item.Value); err != nil { 1742 t.Fatal(err) 1743 } 1744 } 1745 return nil 1746 }); err != nil { 1747 t.Fatal(err) 1748 } 1749 1750 // Remove items one at a time and check consistency. 1751 for _, item := range items { 1752 if err := db.Update(func(tx *memcache.Tx) error { 1753 return tx.Bucket([]byte("widgets")).Delete(item.Key) 1754 }); err != nil { 1755 t.Fatal(err) 1756 } 1757 } 1758 1759 // Anything before our deletion index should be nil. 1760 if err := db.View(func(tx *memcache.Tx) error { 1761 if err := tx.Bucket([]byte("widgets")).ForEach(func(k, v []byte) error { 1762 t.Fatalf("bucket should be empty; found: %06x", trunc(k, 3)) 1763 return nil 1764 }); err != nil { 1765 t.Fatal(err) 1766 } 1767 return nil 1768 }); err != nil { 1769 t.Fatal(err) 1770 } 1771 1772 return true 1773 }, qconfig()); err != nil { 1774 t.Error(err) 1775 } 1776 } 1777 1778 func ExampleBucket_Put() { 1779 // Open the database. 1780 db, err := memcache.Open(tempfile(), 0666, nil) 1781 if err != nil { 1782 log.Fatal(err) 1783 } 1784 defer os.Remove(db.Path()) 1785 1786 // Start a write transaction. 1787 if err := db.Update(func(tx *memcache.Tx) error { 1788 // Create a bucket. 1789 b, err := tx.CreateBucket([]byte("widgets")) 1790 if err != nil { 1791 return err 1792 } 1793 1794 // Set the value "bar" for the key "foo". 1795 if err := b.Put([]byte("foo"), []byte("bar")); err != nil { 1796 return err 1797 } 1798 return nil 1799 }); err != nil { 1800 log.Fatal(err) 1801 } 1802 1803 // Read value back in a different read-only transaction. 1804 if err := db.View(func(tx *memcache.Tx) error { 1805 value := tx.Bucket([]byte("widgets")).Get([]byte("foo")) 1806 fmt.Printf("The value of 'foo' is: %s\n", value) 1807 return nil 1808 }); err != nil { 1809 log.Fatal(err) 1810 } 1811 1812 // Close database to release file lock. 1813 if err := db.Close(); err != nil { 1814 log.Fatal(err) 1815 } 1816 1817 // Output: 1818 // The value of 'foo' is: bar 1819 } 1820 1821 func ExampleBucket_Delete() { 1822 // Open the database. 1823 db, err := memcache.Open(tempfile(), 0666, nil) 1824 if err != nil { 1825 log.Fatal(err) 1826 } 1827 defer os.Remove(db.Path()) 1828 1829 // Start a write transaction. 1830 if err := db.Update(func(tx *memcache.Tx) error { 1831 // Create a bucket. 1832 b, err := tx.CreateBucket([]byte("widgets")) 1833 if err != nil { 1834 return err 1835 } 1836 1837 // Set the value "bar" for the key "foo". 1838 if err := b.Put([]byte("foo"), []byte("bar")); err != nil { 1839 return err 1840 } 1841 1842 // Retrieve the key back from the database and verify it. 1843 value := b.Get([]byte("foo")) 1844 fmt.Printf("The value of 'foo' was: %s\n", value) 1845 1846 return nil 1847 }); err != nil { 1848 log.Fatal(err) 1849 } 1850 1851 // Delete the key in a different write transaction. 1852 if err := db.Update(func(tx *memcache.Tx) error { 1853 return tx.Bucket([]byte("widgets")).Delete([]byte("foo")) 1854 }); err != nil { 1855 log.Fatal(err) 1856 } 1857 1858 // Retrieve the key again. 1859 if err := db.View(func(tx *memcache.Tx) error { 1860 value := tx.Bucket([]byte("widgets")).Get([]byte("foo")) 1861 if value == nil { 1862 fmt.Printf("The value of 'foo' is now: nil\n") 1863 } 1864 return nil 1865 }); err != nil { 1866 log.Fatal(err) 1867 } 1868 1869 // Close database to release file lock. 1870 if err := db.Close(); err != nil { 1871 log.Fatal(err) 1872 } 1873 1874 // Output: 1875 // The value of 'foo' was: bar 1876 // The value of 'foo' is now: nil 1877 } 1878 1879 func ExampleBucket_ForEach() { 1880 // Open the database. 1881 db, err := memcache.Open(tempfile(), 0666, nil) 1882 if err != nil { 1883 log.Fatal(err) 1884 } 1885 defer os.Remove(db.Path()) 1886 1887 // Insert data into a bucket. 1888 if err := db.Update(func(tx *memcache.Tx) error { 1889 b, err := tx.CreateBucket([]byte("animals")) 1890 if err != nil { 1891 return err 1892 } 1893 1894 if err := b.Put([]byte("dog"), []byte("fun")); err != nil { 1895 return err 1896 } 1897 if err := b.Put([]byte("cat"), []byte("lame")); err != nil { 1898 return err 1899 } 1900 if err := b.Put([]byte("liger"), []byte("awesome")); err != nil { 1901 return err 1902 } 1903 1904 // Iterate over items in sorted key order. 1905 if err := b.ForEach(func(k, v []byte) error { 1906 fmt.Printf("A %s is %s.\n", k, v) 1907 return nil 1908 }); err != nil { 1909 return err 1910 } 1911 1912 return nil 1913 }); err != nil { 1914 log.Fatal(err) 1915 } 1916 1917 // Close database to release file lock. 1918 if err := db.Close(); err != nil { 1919 log.Fatal(err) 1920 } 1921 1922 // Output: 1923 // A cat is lame. 1924 // A dog is fun. 1925 // A liger is awesome. 1926 }