github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/bitree/bdb/tx_test.go (about) 1 // Copyright 2021 The Bitalosdb author(hustxrb@163.com) and other contributors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package bdb_test 16 17 import ( 18 "bytes" 19 "errors" 20 "fmt" 21 "log" 22 "os" 23 "testing" 24 25 "github.com/zuoyebang/bitalosdb/bitree/bdb" 26 "github.com/zuoyebang/bitalosdb/internal/options" 27 ) 28 29 func TestTx_Check_ReadOnly(t *testing.T) { 30 db := MustOpenDB() 31 defer db.Close() 32 if err := db.Update(func(tx *bdb.Tx) error { 33 b, err := tx.CreateBucket([]byte("widgets")) 34 if err != nil { 35 t.Fatal(err) 36 } 37 if err := b.Put([]byte("foo"), []byte("bar")); err != nil { 38 t.Fatal(err) 39 } 40 return nil 41 }); err != nil { 42 t.Fatal(err) 43 } 44 if err := db.DB.Close(); err != nil { 45 t.Fatal(err) 46 } 47 48 opts := options.DefaultBdbOptions 49 opts.ReadOnly = true 50 readOnlyDB, err := bdb.Open(db.f, opts) 51 if err != nil { 52 t.Fatal(err) 53 } 54 defer readOnlyDB.Close() 55 56 tx, err := readOnlyDB.Begin(false) 57 if err != nil { 58 t.Fatal(err) 59 } 60 numChecks := 2 61 errc := make(chan error, numChecks) 62 check := func() { 63 errc <- <-tx.Check() 64 } 65 66 for i := 0; i < numChecks; i++ { 67 go check() 68 } 69 for i := 0; i < numChecks; i++ { 70 if err := <-errc; err != nil { 71 t.Fatal(err) 72 } 73 } 74 75 tx.Rollback() 76 } 77 78 func TestTx_Commit_ErrTxClosed(t *testing.T) { 79 db := MustOpenDB() 80 defer db.MustClose() 81 tx, err := db.Begin(true) 82 if err != nil { 83 t.Fatal(err) 84 } 85 86 if _, err := tx.CreateBucket([]byte("foo")); err != nil { 87 t.Fatal(err) 88 } 89 90 if err := tx.Commit(); err != nil { 91 t.Fatal(err) 92 } 93 94 if err := tx.Commit(); err != bdb.ErrTxClosed { 95 t.Fatalf("unexpected error: %s", err) 96 } 97 } 98 99 func TestTx_Rollback_ErrTxClosed(t *testing.T) { 100 db := MustOpenDB() 101 defer db.MustClose() 102 103 tx, err := db.Begin(true) 104 if err != nil { 105 t.Fatal(err) 106 } 107 108 if err := tx.Rollback(); err != nil { 109 t.Fatal(err) 110 } 111 if err := tx.Rollback(); err != bdb.ErrTxClosed { 112 t.Fatalf("unexpected error: %s", err) 113 } 114 } 115 116 func TestTx_Commit_ErrTxNotWritable(t *testing.T) { 117 db := MustOpenDB() 118 defer db.MustClose() 119 tx, err := db.Begin(false) 120 if err != nil { 121 t.Fatal(err) 122 } 123 if err := tx.Commit(); err != bdb.ErrTxNotWritable { 124 t.Fatal(err) 125 } 126 tx.Rollback() 127 } 128 129 // Ensure that a transaction can retrieve a cursor on the root bucket. 130 func TestTx_Cursor(t *testing.T) { 131 db := MustOpenDB() 132 defer db.MustClose() 133 if err := db.Update(func(tx *bdb.Tx) error { 134 if _, err := tx.CreateBucket([]byte("widgets")); err != nil { 135 t.Fatal(err) 136 } 137 138 if _, err := tx.CreateBucket([]byte("woojits")); err != nil { 139 t.Fatal(err) 140 } 141 142 c := tx.Cursor() 143 if k, v := c.First(); !bytes.Equal(k, []byte("widgets")) { 144 t.Fatalf("unexpected key: %v", k) 145 } else if v != nil { 146 t.Fatalf("unexpected value: %v", v) 147 } 148 149 if k, v := c.Next(); !bytes.Equal(k, []byte("woojits")) { 150 t.Fatalf("unexpected key: %v", k) 151 } else if v != nil { 152 t.Fatalf("unexpected value: %v", v) 153 } 154 155 if k, v := c.Next(); k != nil { 156 t.Fatalf("unexpected key: %v", k) 157 } else if v != nil { 158 t.Fatalf("unexpected value: %v", k) 159 } 160 161 return nil 162 }); err != nil { 163 t.Fatal(err) 164 } 165 } 166 167 // Ensure that creating a bucket with a read-only transaction returns an error. 168 func TestTx_CreateBucket_ErrTxNotWritable(t *testing.T) { 169 db := MustOpenDB() 170 defer db.MustClose() 171 if err := db.View(func(tx *bdb.Tx) error { 172 _, err := tx.CreateBucket([]byte("foo")) 173 if err != bdb.ErrTxNotWritable { 174 t.Fatalf("unexpected error: %s", err) 175 } 176 return nil 177 }); err != nil { 178 t.Fatal(err) 179 } 180 } 181 182 func TestTx_CreateBucket_ErrTxClosed(t *testing.T) { 183 db := MustOpenDB() 184 defer db.MustClose() 185 tx, err := db.Begin(true) 186 if err != nil { 187 t.Fatal(err) 188 } 189 if err := tx.Commit(); err != nil { 190 t.Fatal(err) 191 } 192 193 if _, err := tx.CreateBucket([]byte("foo")); err != bdb.ErrTxClosed { 194 t.Fatalf("unexpected error: %s", err) 195 } 196 } 197 198 func TestTx_Bucket(t *testing.T) { 199 db := MustOpenDB() 200 defer db.MustClose() 201 if err := db.Update(func(tx *bdb.Tx) error { 202 if _, err := tx.CreateBucket([]byte("widgets")); err != nil { 203 t.Fatal(err) 204 } 205 if tx.Bucket([]byte("widgets")) == nil { 206 t.Fatal("expected bucket") 207 } 208 return nil 209 }); err != nil { 210 t.Fatal(err) 211 } 212 } 213 214 func TestTx_Get_NotFound(t *testing.T) { 215 db := MustOpenDB() 216 defer db.MustClose() 217 if err := db.Update(func(tx *bdb.Tx) error { 218 b, err := tx.CreateBucket([]byte("widgets")) 219 if err != nil { 220 t.Fatal(err) 221 } 222 223 if err := b.Put([]byte("foo"), []byte("bar")); err != nil { 224 t.Fatal(err) 225 } 226 if b.Get([]byte("no_such_key")) != nil { 227 t.Fatal("expected nil value") 228 } 229 return nil 230 }); err != nil { 231 t.Fatal(err) 232 } 233 } 234 235 func TestTx_CreateBucket(t *testing.T) { 236 db := MustOpenDB() 237 defer db.MustClose() 238 239 if err := db.Update(func(tx *bdb.Tx) error { 240 b, err := tx.CreateBucket([]byte("widgets")) 241 if err != nil { 242 t.Fatal(err) 243 } else if b == nil { 244 t.Fatal("expected bucket") 245 } 246 return nil 247 }); err != nil { 248 t.Fatal(err) 249 } 250 251 if err := db.View(func(tx *bdb.Tx) error { 252 if tx.Bucket([]byte("widgets")) == nil { 253 t.Fatal("expected bucket") 254 } 255 return nil 256 }); err != nil { 257 t.Fatal(err) 258 } 259 } 260 261 func TestTx_CreateBucketIfNotExists(t *testing.T) { 262 db := MustOpenDB() 263 defer db.MustClose() 264 if err := db.Update(func(tx *bdb.Tx) error { 265 if b, err := tx.CreateBucketIfNotExists([]byte("widgets")); err != nil { 266 t.Fatal(err) 267 } else if b == nil { 268 t.Fatal("expected bucket") 269 } 270 271 if b, err := tx.CreateBucketIfNotExists([]byte("widgets")); err != nil { 272 t.Fatal(err) 273 } else if b == nil { 274 t.Fatal("expected bucket") 275 } 276 277 return nil 278 }); err != nil { 279 t.Fatal(err) 280 } 281 282 if err := db.View(func(tx *bdb.Tx) error { 283 if tx.Bucket([]byte("widgets")) == nil { 284 t.Fatal("expected bucket") 285 } 286 return nil 287 }); err != nil { 288 t.Fatal(err) 289 } 290 } 291 292 func TestTx_CreateBucketIfNotExists_ErrBucketNameRequired(t *testing.T) { 293 db := MustOpenDB() 294 defer db.MustClose() 295 if err := db.Update(func(tx *bdb.Tx) error { 296 if _, err := tx.CreateBucketIfNotExists([]byte{}); err != bdb.ErrBucketNameRequired { 297 t.Fatalf("unexpected error: %s", err) 298 } 299 300 if _, err := tx.CreateBucketIfNotExists(nil); err != bdb.ErrBucketNameRequired { 301 t.Fatalf("unexpected error: %s", err) 302 } 303 304 return nil 305 }); err != nil { 306 t.Fatal(err) 307 } 308 } 309 310 func TestTx_CreateBucket_ErrBucketExists(t *testing.T) { 311 db := MustOpenDB() 312 defer db.MustClose() 313 314 if err := db.Update(func(tx *bdb.Tx) error { 315 if _, err := tx.CreateBucket([]byte("widgets")); err != nil { 316 t.Fatal(err) 317 } 318 return nil 319 }); err != nil { 320 t.Fatal(err) 321 } 322 323 if err := db.Update(func(tx *bdb.Tx) error { 324 if _, err := tx.CreateBucket([]byte("widgets")); err != bdb.ErrBucketExists { 325 t.Fatalf("unexpected error: %s", err) 326 } 327 return nil 328 }); err != nil { 329 t.Fatal(err) 330 } 331 } 332 333 func TestTx_CreateBucket_ErrBucketNameRequired(t *testing.T) { 334 db := MustOpenDB() 335 defer db.MustClose() 336 if err := db.Update(func(tx *bdb.Tx) error { 337 if _, err := tx.CreateBucket(nil); err != bdb.ErrBucketNameRequired { 338 t.Fatalf("unexpected error: %s", err) 339 } 340 return nil 341 }); err != nil { 342 t.Fatal(err) 343 } 344 } 345 346 func TestTx_DeleteBucket(t *testing.T) { 347 db := MustOpenDB() 348 defer db.MustClose() 349 350 if err := db.Update(func(tx *bdb.Tx) error { 351 b, err := tx.CreateBucket([]byte("widgets")) 352 if err != nil { 353 t.Fatal(err) 354 } 355 if err := b.Put([]byte("foo"), []byte("bar")); err != nil { 356 t.Fatal(err) 357 } 358 return nil 359 }); err != nil { 360 t.Fatal(err) 361 } 362 363 if err := db.Update(func(tx *bdb.Tx) error { 364 if err := tx.DeleteBucket([]byte("widgets")); err != nil { 365 t.Fatal(err) 366 } 367 if tx.Bucket([]byte("widgets")) != nil { 368 t.Fatal("unexpected bucket") 369 } 370 return nil 371 }); err != nil { 372 t.Fatal(err) 373 } 374 375 if err := db.Update(func(tx *bdb.Tx) error { 376 b, err := tx.CreateBucket([]byte("widgets")) 377 if err != nil { 378 t.Fatal(err) 379 } 380 if v := b.Get([]byte("foo")); v != nil { 381 t.Fatalf("unexpected phantom value: %v", v) 382 } 383 return nil 384 }); err != nil { 385 t.Fatal(err) 386 } 387 } 388 389 func TestTx_DeleteBucket_ErrTxClosed(t *testing.T) { 390 db := MustOpenDB() 391 defer db.MustClose() 392 tx, err := db.Begin(true) 393 if err != nil { 394 t.Fatal(err) 395 } 396 if err := tx.Commit(); err != nil { 397 t.Fatal(err) 398 } 399 if err := tx.DeleteBucket([]byte("foo")); err != bdb.ErrTxClosed { 400 t.Fatalf("unexpected error: %s", err) 401 } 402 } 403 404 func TestTx_DeleteBucket_ReadOnly(t *testing.T) { 405 db := MustOpenDB() 406 defer db.MustClose() 407 if err := db.View(func(tx *bdb.Tx) error { 408 if err := tx.DeleteBucket([]byte("foo")); err != bdb.ErrTxNotWritable { 409 t.Fatalf("unexpected error: %s", err) 410 } 411 return nil 412 }); err != nil { 413 t.Fatal(err) 414 } 415 } 416 417 func TestTx_DeleteBucket_NotFound(t *testing.T) { 418 db := MustOpenDB() 419 defer db.MustClose() 420 if err := db.Update(func(tx *bdb.Tx) error { 421 if err := tx.DeleteBucket([]byte("widgets")); err != bdb.ErrBucketNotFound { 422 t.Fatalf("unexpected error: %s", err) 423 } 424 return nil 425 }); err != nil { 426 t.Fatal(err) 427 } 428 } 429 430 func TestTx_ForEach_NoError(t *testing.T) { 431 db := MustOpenDB() 432 defer db.MustClose() 433 if err := db.Update(func(tx *bdb.Tx) error { 434 b, err := tx.CreateBucket([]byte("widgets")) 435 if err != nil { 436 t.Fatal(err) 437 } 438 if err := b.Put([]byte("foo"), []byte("bar")); err != nil { 439 t.Fatal(err) 440 } 441 442 if err := tx.ForEach(func(name []byte, b *bdb.Bucket) error { 443 return nil 444 }); err != nil { 445 t.Fatal(err) 446 } 447 return nil 448 }); err != nil { 449 t.Fatal(err) 450 } 451 } 452 453 // Ensure that an error is returned when a tx.ForEach function returns an error. 454 func TestTx_ForEach_WithError(t *testing.T) { 455 db := MustOpenDB() 456 defer db.MustClose() 457 if err := db.Update(func(tx *bdb.Tx) error { 458 b, err := tx.CreateBucket([]byte("widgets")) 459 if err != nil { 460 t.Fatal(err) 461 } 462 if err := b.Put([]byte("foo"), []byte("bar")); err != nil { 463 t.Fatal(err) 464 } 465 466 marker := errors.New("marker") 467 if err := tx.ForEach(func(name []byte, b *bdb.Bucket) error { 468 return marker 469 }); err != marker { 470 t.Fatalf("unexpected error: %s", err) 471 } 472 return nil 473 }); err != nil { 474 t.Fatal(err) 475 } 476 } 477 478 func TestTx_OnCommit(t *testing.T) { 479 db := MustOpenDB() 480 defer db.MustClose() 481 482 var x int 483 if err := db.Update(func(tx *bdb.Tx) error { 484 tx.OnCommit(func() { x += 1 }) 485 tx.OnCommit(func() { x += 2 }) 486 if _, err := tx.CreateBucket([]byte("widgets")); err != nil { 487 t.Fatal(err) 488 } 489 return nil 490 }); err != nil { 491 t.Fatal(err) 492 } else if x != 3 { 493 t.Fatalf("unexpected x: %d", x) 494 } 495 } 496 497 func TestTx_OnCommit_Rollback(t *testing.T) { 498 db := MustOpenDB() 499 defer db.MustClose() 500 501 var x int 502 if err := db.Update(func(tx *bdb.Tx) error { 503 tx.OnCommit(func() { x += 1 }) 504 tx.OnCommit(func() { x += 2 }) 505 if _, err := tx.CreateBucket([]byte("widgets")); err != nil { 506 t.Fatal(err) 507 } 508 return errors.New("rollback this commit") 509 }); err == nil || err.Error() != "rollback this commit" { 510 t.Fatalf("unexpected error: %s", err) 511 } else if x != 0 { 512 t.Fatalf("unexpected x: %d", x) 513 } 514 } 515 516 func TestTx_CopyFile(t *testing.T) { 517 db := MustOpenDB() 518 defer db.MustClose() 519 520 path := tempfile() 521 if err := db.Update(func(tx *bdb.Tx) error { 522 b, err := tx.CreateBucket([]byte("widgets")) 523 if err != nil { 524 t.Fatal(err) 525 } 526 if err := b.Put([]byte("foo"), []byte("bar")); err != nil { 527 t.Fatal(err) 528 } 529 if err := b.Put([]byte("baz"), []byte("bat")); err != nil { 530 t.Fatal(err) 531 } 532 return nil 533 }); err != nil { 534 t.Fatal(err) 535 } 536 537 if err := db.View(func(tx *bdb.Tx) error { 538 return tx.CopyFile(path, 0600) 539 }); err != nil { 540 t.Fatal(err) 541 } 542 543 db2, err := bdb.Open(path, nil) 544 if err != nil { 545 t.Fatal(err) 546 } 547 548 if err := db2.View(func(tx *bdb.Tx) error { 549 if v := tx.Bucket([]byte("widgets")).Get([]byte("foo")); !bytes.Equal(v, []byte("bar")) { 550 t.Fatalf("unexpected value: %v", v) 551 } 552 if v := tx.Bucket([]byte("widgets")).Get([]byte("baz")); !bytes.Equal(v, []byte("bat")) { 553 t.Fatalf("unexpected value: %v", v) 554 } 555 return nil 556 }); err != nil { 557 t.Fatal(err) 558 } 559 560 if err := db2.Close(); err != nil { 561 t.Fatal(err) 562 } 563 } 564 565 type failWriterError struct{} 566 567 func (failWriterError) Error() string { 568 return "error injected for tests" 569 } 570 571 type failWriter struct { 572 After int 573 } 574 575 func (f *failWriter) Write(p []byte) (n int, err error) { 576 n = len(p) 577 if n > f.After { 578 n = f.After 579 err = failWriterError{} 580 } 581 f.After -= n 582 return n, err 583 } 584 585 func TestTx_CopyFile_Error_Meta(t *testing.T) { 586 db := MustOpenDB() 587 defer db.MustClose() 588 if err := db.Update(func(tx *bdb.Tx) error { 589 b, err := tx.CreateBucket([]byte("widgets")) 590 if err != nil { 591 t.Fatal(err) 592 } 593 if err := b.Put([]byte("foo"), []byte("bar")); err != nil { 594 t.Fatal(err) 595 } 596 if err := b.Put([]byte("baz"), []byte("bat")); err != nil { 597 t.Fatal(err) 598 } 599 return nil 600 }); err != nil { 601 t.Fatal(err) 602 } 603 604 if err := db.View(func(tx *bdb.Tx) error { 605 return tx.Copy(&failWriter{}) 606 }); err == nil || err.Error() != "meta 0 copy: error injected for tests" { 607 t.Fatalf("unexpected error: %v", err) 608 } 609 } 610 611 func TestTx_CopyFile_Error_Normal(t *testing.T) { 612 db := MustOpenDB() 613 defer db.MustClose() 614 if err := db.Update(func(tx *bdb.Tx) error { 615 b, err := tx.CreateBucket([]byte("widgets")) 616 if err != nil { 617 t.Fatal(err) 618 } 619 if err := b.Put([]byte("foo"), []byte("bar")); err != nil { 620 t.Fatal(err) 621 } 622 if err := b.Put([]byte("baz"), []byte("bat")); err != nil { 623 t.Fatal(err) 624 } 625 return nil 626 }); err != nil { 627 t.Fatal(err) 628 } 629 630 if err := db.View(func(tx *bdb.Tx) error { 631 return tx.Copy(&failWriter{3 * db.Info().PageSize}) 632 }); err == nil || err.Error() != "error injected for tests" { 633 t.Fatalf("unexpected error: %v", err) 634 } 635 } 636 637 func TestTx_Rollback(t *testing.T) { 638 for _, isSyncFreelist := range []bool{false, true} { 639 db, err := bdb.Open(tempfile(), nil) 640 if err != nil { 641 log.Fatal(err) 642 } 643 defer os.Remove(db.Path()) 644 db.NoFreelistSync = isSyncFreelist 645 646 tx, err := db.Begin(true) 647 if err != nil { 648 t.Fatalf("Error starting tx: %v", err) 649 } 650 bucket := []byte("mybucket") 651 if _, err := tx.CreateBucket(bucket); err != nil { 652 t.Fatalf("Error creating bucket: %v", err) 653 } 654 if err := tx.Commit(); err != nil { 655 t.Fatalf("Error on commit: %v", err) 656 } 657 658 tx, err = db.Begin(true) 659 if err != nil { 660 t.Fatalf("Error starting tx: %v", err) 661 } 662 b := tx.Bucket(bucket) 663 if err := b.Put([]byte("k"), []byte("v")); err != nil { 664 t.Fatalf("Error on put: %v", err) 665 } 666 667 if err := tx.Rollback(); err != nil { 668 t.Fatalf("Error on rollback: %v", err) 669 } 670 671 tx, err = db.Begin(false) 672 if err != nil { 673 t.Fatalf("Error starting tx: %v", err) 674 } 675 b = tx.Bucket(bucket) 676 if v := b.Get([]byte("k")); v != nil { 677 t.Fatalf("Value for k should not have been stored") 678 } 679 if err := tx.Rollback(); err != nil { 680 t.Fatalf("Error on rollback: %v", err) 681 } 682 683 } 684 } 685 686 func TestTx_releaseRange(t *testing.T) { 687 opts := options.DefaultBdbOptions 688 opts.InitialMmapSize = os.Getpagesize() * 100 689 db := MustOpenWithOption(opts) 690 defer db.MustClose() 691 692 bucket := "bucket" 693 694 put := func(key, value string) { 695 if err := db.Update(func(tx *bdb.Tx) error { 696 b, err := tx.CreateBucketIfNotExists([]byte(bucket)) 697 if err != nil { 698 t.Fatal(err) 699 } 700 return b.Put([]byte(key), []byte(value)) 701 }); err != nil { 702 t.Fatal(err) 703 } 704 } 705 706 del := func(key string) { 707 if err := db.Update(func(tx *bdb.Tx) error { 708 b, err := tx.CreateBucketIfNotExists([]byte(bucket)) 709 if err != nil { 710 t.Fatal(err) 711 } 712 return b.Delete([]byte(key)) 713 }); err != nil { 714 t.Fatal(err) 715 } 716 } 717 718 getWithTxn := func(txn *bdb.Tx, key string) []byte { 719 return txn.Bucket([]byte(bucket)).Get([]byte(key)) 720 } 721 722 openReadTxn := func() *bdb.Tx { 723 readTx, err := db.Begin(false) 724 if err != nil { 725 t.Fatal(err) 726 } 727 return readTx 728 } 729 730 checkWithReadTxn := func(txn *bdb.Tx, key string, wantValue []byte) { 731 value := getWithTxn(txn, key) 732 if !bytes.Equal(value, wantValue) { 733 t.Errorf("Wanted value to be %s for key %s, but got %s", wantValue, key, string(value)) 734 } 735 } 736 737 rollback := func(txn *bdb.Tx) { 738 if err := txn.Rollback(); err != nil { 739 t.Fatal(err) 740 } 741 } 742 743 put("k1", "v1") 744 rtx1 := openReadTxn() 745 put("k2", "v2") 746 hold1 := openReadTxn() 747 put("k3", "v3") 748 hold2 := openReadTxn() 749 del("k3") 750 rtx2 := openReadTxn() 751 del("k1") 752 hold3 := openReadTxn() 753 del("k2") 754 hold4 := openReadTxn() 755 put("k4", "v4") 756 hold5 := openReadTxn() 757 758 rollback(hold1) 759 rollback(hold2) 760 rollback(hold3) 761 rollback(hold4) 762 rollback(hold5) 763 764 put("k4", "v4") 765 766 checkWithReadTxn(rtx1, "k1", []byte("v1")) 767 checkWithReadTxn(rtx2, "k2", []byte("v2")) 768 rollback(rtx1) 769 rollback(rtx2) 770 771 rtx7 := openReadTxn() 772 checkWithReadTxn(rtx7, "k1", nil) 773 checkWithReadTxn(rtx7, "k2", nil) 774 checkWithReadTxn(rtx7, "k3", nil) 775 checkWithReadTxn(rtx7, "k4", []byte("v4")) 776 rollback(rtx7) 777 } 778 779 func ExampleTx_Rollback() { 780 db, err := bdb.Open(tempfile(), nil) 781 if err != nil { 782 log.Fatal(err) 783 } 784 defer os.Remove(db.Path()) 785 786 if err := db.Update(func(tx *bdb.Tx) error { 787 _, err := tx.CreateBucket([]byte("widgets")) 788 return err 789 }); err != nil { 790 log.Fatal(err) 791 } 792 793 if err := db.Update(func(tx *bdb.Tx) error { 794 return tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")) 795 }); err != nil { 796 log.Fatal(err) 797 } 798 799 tx, err := db.Begin(true) 800 if err != nil { 801 log.Fatal(err) 802 } 803 b := tx.Bucket([]byte("widgets")) 804 if err := b.Put([]byte("foo"), []byte("baz")); err != nil { 805 log.Fatal(err) 806 } 807 if err := tx.Rollback(); err != nil { 808 log.Fatal(err) 809 } 810 811 if err := db.View(func(tx *bdb.Tx) error { 812 value := tx.Bucket([]byte("widgets")).Get([]byte("foo")) 813 fmt.Printf("The value for 'foo' is still: %s\n", value) 814 return nil 815 }); err != nil { 816 log.Fatal(err) 817 } 818 819 if err := db.Close(); err != nil { 820 log.Fatal(err) 821 } 822 } 823 824 func ExampleTx_CopyFile() { 825 db, err := bdb.Open(tempfile(), nil) 826 if err != nil { 827 log.Fatal(err) 828 } 829 defer os.Remove(db.Path()) 830 831 if err := db.Update(func(tx *bdb.Tx) error { 832 b, err := tx.CreateBucket([]byte("widgets")) 833 if err != nil { 834 return err 835 } 836 if err := b.Put([]byte("foo"), []byte("bar")); err != nil { 837 return err 838 } 839 return nil 840 }); err != nil { 841 log.Fatal(err) 842 } 843 844 toFile := tempfile() 845 if err := db.View(func(tx *bdb.Tx) error { 846 return tx.CopyFile(toFile, 0666) 847 }); err != nil { 848 log.Fatal(err) 849 } 850 defer os.Remove(toFile) 851 852 db2, err := bdb.Open(toFile, nil) 853 if err != nil { 854 log.Fatal(err) 855 } 856 857 if err := db2.View(func(tx *bdb.Tx) error { 858 value := tx.Bucket([]byte("widgets")).Get([]byte("foo")) 859 fmt.Printf("The value for 'foo' in the clone is: %s\n", value) 860 return nil 861 }); err != nil { 862 log.Fatal(err) 863 } 864 865 if err := db.Close(); err != nil { 866 log.Fatal(err) 867 } 868 869 if err := db2.Close(); err != nil { 870 log.Fatal(err) 871 } 872 }