1 package bbolt_test
2
3 import (
4 "bytes"
5 "errors"
6 "fmt"
7 "log"
8 "os"
9 "runtime"
10 "testing"
11 "time"
12
13 "github.com/stretchr/testify/assert"
14 "github.com/stretchr/testify/require"
15
16 bolt "go.etcd.io/bbolt"
17 "go.etcd.io/bbolt/internal/btesting"
18 )
19
20
21 func TestTx_Check_ReadOnly(t *testing.T) {
22 db := btesting.MustCreateDB(t)
23 if err := db.Update(func(tx *bolt.Tx) error {
24 b, err := tx.CreateBucket([]byte("widgets"))
25 if err != nil {
26 t.Fatal(err)
27 }
28 if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
29 t.Fatal(err)
30 }
31 return nil
32 }); err != nil {
33 t.Fatal(err)
34 }
35 if err := db.Close(); err != nil {
36 t.Fatal(err)
37 }
38
39 readOnlyDB, err := bolt.Open(db.Path(), 0666, &bolt.Options{ReadOnly: true})
40 if err != nil {
41 t.Fatal(err)
42 }
43 defer readOnlyDB.Close()
44
45 tx, err := readOnlyDB.Begin(false)
46 if err != nil {
47 t.Fatal(err)
48 }
49
50 numChecks := 2
51 errc := make(chan error, numChecks)
52 check := func() {
53 errc <- <-tx.Check()
54 }
55
56 for i := 0; i < numChecks; i++ {
57 go check()
58 }
59 for i := 0; i < numChecks; i++ {
60 if err := <-errc; err != nil {
61 t.Fatal(err)
62 }
63 }
64
65 err = tx.Rollback()
66 if err != nil {
67 t.Fatal(err)
68 }
69 }
70
71
72 func TestTx_Commit_ErrTxClosed(t *testing.T) {
73 db := btesting.MustCreateDB(t)
74 tx, err := db.Begin(true)
75 if err != nil {
76 t.Fatal(err)
77 }
78
79 if _, err := tx.CreateBucket([]byte("foo")); err != nil {
80 t.Fatal(err)
81 }
82
83 if err := tx.Commit(); err != nil {
84 t.Fatal(err)
85 }
86
87 if err := tx.Commit(); err != bolt.ErrTxClosed {
88 t.Fatalf("unexpected error: %s", err)
89 }
90 }
91
92
93 func TestTx_Rollback_ErrTxClosed(t *testing.T) {
94 db := btesting.MustCreateDB(t)
95
96 tx, err := db.Begin(true)
97 if err != nil {
98 t.Fatal(err)
99 }
100
101 if err := tx.Rollback(); err != nil {
102 t.Fatal(err)
103 }
104 if err := tx.Rollback(); err != bolt.ErrTxClosed {
105 t.Fatalf("unexpected error: %s", err)
106 }
107 }
108
109
110 func TestTx_Commit_ErrTxNotWritable(t *testing.T) {
111 db := btesting.MustCreateDB(t)
112 tx, err := db.Begin(false)
113 if err != nil {
114 t.Fatal(err)
115 }
116 if err := tx.Commit(); err != bolt.ErrTxNotWritable {
117 t.Fatal(err)
118 }
119
120 err = tx.Rollback()
121 if err != nil {
122 t.Fatal(err)
123 }
124 }
125
126
127 func TestTx_Cursor(t *testing.T) {
128 db := btesting.MustCreateDB(t)
129 if err := db.Update(func(tx *bolt.Tx) error {
130 if _, err := tx.CreateBucket([]byte("widgets")); err != nil {
131 t.Fatal(err)
132 }
133
134 if _, err := tx.CreateBucket([]byte("woojits")); err != nil {
135 t.Fatal(err)
136 }
137
138 c := tx.Cursor()
139 if k, v := c.First(); !bytes.Equal(k, []byte("widgets")) {
140 t.Fatalf("unexpected key: %v", k)
141 } else if v != nil {
142 t.Fatalf("unexpected value: %v", v)
143 }
144
145 if k, v := c.Next(); !bytes.Equal(k, []byte("woojits")) {
146 t.Fatalf("unexpected key: %v", k)
147 } else if v != nil {
148 t.Fatalf("unexpected value: %v", v)
149 }
150
151 if k, v := c.Next(); k != nil {
152 t.Fatalf("unexpected key: %v", k)
153 } else if v != nil {
154 t.Fatalf("unexpected value: %v", k)
155 }
156
157 return nil
158 }); err != nil {
159 t.Fatal(err)
160 }
161 }
162
163
164 func TestTx_CreateBucket_ErrTxNotWritable(t *testing.T) {
165 db := btesting.MustCreateDB(t)
166 if err := db.View(func(tx *bolt.Tx) error {
167 _, err := tx.CreateBucket([]byte("foo"))
168 if err != bolt.ErrTxNotWritable {
169 t.Fatalf("unexpected error: %s", err)
170 }
171 return nil
172 }); err != nil {
173 t.Fatal(err)
174 }
175 }
176
177
178 func TestTx_CreateBucket_ErrTxClosed(t *testing.T) {
179 db := btesting.MustCreateDB(t)
180 tx, err := db.Begin(true)
181 if err != nil {
182 t.Fatal(err)
183 }
184 if err := tx.Commit(); err != nil {
185 t.Fatal(err)
186 }
187
188 if _, err := tx.CreateBucket([]byte("foo")); err != bolt.ErrTxClosed {
189 t.Fatalf("unexpected error: %s", err)
190 }
191 }
192
193
194 func TestTx_Bucket(t *testing.T) {
195 db := btesting.MustCreateDB(t)
196 if err := db.Update(func(tx *bolt.Tx) error {
197 if _, err := tx.CreateBucket([]byte("widgets")); err != nil {
198 t.Fatal(err)
199 }
200 if tx.Bucket([]byte("widgets")) == nil {
201 t.Fatal("expected bucket")
202 }
203 return nil
204 }); err != nil {
205 t.Fatal(err)
206 }
207 }
208
209
210 func TestTx_Get_NotFound(t *testing.T) {
211 db := btesting.MustCreateDB(t)
212 if err := db.Update(func(tx *bolt.Tx) error {
213 b, err := tx.CreateBucket([]byte("widgets"))
214 if err != nil {
215 t.Fatal(err)
216 }
217
218 if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
219 t.Fatal(err)
220 }
221 if b.Get([]byte("no_such_key")) != nil {
222 t.Fatal("expected nil value")
223 }
224 return nil
225 }); err != nil {
226 t.Fatal(err)
227 }
228 }
229
230
231 func TestTx_CreateBucket(t *testing.T) {
232 db := btesting.MustCreateDB(t)
233
234
235 if err := db.Update(func(tx *bolt.Tx) error {
236 b, err := tx.CreateBucket([]byte("widgets"))
237 if err != nil {
238 t.Fatal(err)
239 } else if b == nil {
240 t.Fatal("expected bucket")
241 }
242 return nil
243 }); err != nil {
244 t.Fatal(err)
245 }
246
247
248 if err := db.View(func(tx *bolt.Tx) error {
249 if tx.Bucket([]byte("widgets")) == nil {
250 t.Fatal("expected bucket")
251 }
252 return nil
253 }); err != nil {
254 t.Fatal(err)
255 }
256 }
257
258
259 func TestTx_CreateBucketIfNotExists(t *testing.T) {
260 db := btesting.MustCreateDB(t)
261 if err := db.Update(func(tx *bolt.Tx) error {
262
263 if b, err := tx.CreateBucketIfNotExists([]byte("widgets")); err != nil {
264 t.Fatal(err)
265 } else if b == nil {
266 t.Fatal("expected bucket")
267 }
268
269
270 if b, err := tx.CreateBucketIfNotExists([]byte("widgets")); err != nil {
271 t.Fatal(err)
272 } else if b == nil {
273 t.Fatal("expected bucket")
274 }
275
276 return nil
277 }); err != nil {
278 t.Fatal(err)
279 }
280
281
282 if err := db.View(func(tx *bolt.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
293 func TestTx_CreateBucketIfNotExists_ErrBucketNameRequired(t *testing.T) {
294 db := btesting.MustCreateDB(t)
295 if err := db.Update(func(tx *bolt.Tx) error {
296 if _, err := tx.CreateBucketIfNotExists([]byte{}); err != bolt.ErrBucketNameRequired {
297 t.Fatalf("unexpected error: %s", err)
298 }
299
300 if _, err := tx.CreateBucketIfNotExists(nil); err != bolt.ErrBucketNameRequired {
301 t.Fatalf("unexpected error: %s", err)
302 }
303
304 return nil
305 }); err != nil {
306 t.Fatal(err)
307 }
308 }
309
310
311 func TestTx_CreateBucket_ErrBucketExists(t *testing.T) {
312 db := btesting.MustCreateDB(t)
313
314
315 if err := db.Update(func(tx *bolt.Tx) error {
316 if _, err := tx.CreateBucket([]byte("widgets")); err != nil {
317 t.Fatal(err)
318 }
319 return nil
320 }); err != nil {
321 t.Fatal(err)
322 }
323
324
325 if err := db.Update(func(tx *bolt.Tx) error {
326 if _, err := tx.CreateBucket([]byte("widgets")); err != bolt.ErrBucketExists {
327 t.Fatalf("unexpected error: %s", err)
328 }
329 return nil
330 }); err != nil {
331 t.Fatal(err)
332 }
333 }
334
335
336 func TestTx_CreateBucket_ErrBucketNameRequired(t *testing.T) {
337 db := btesting.MustCreateDB(t)
338 if err := db.Update(func(tx *bolt.Tx) error {
339 if _, err := tx.CreateBucket(nil); err != bolt.ErrBucketNameRequired {
340 t.Fatalf("unexpected error: %s", err)
341 }
342 return nil
343 }); err != nil {
344 t.Fatal(err)
345 }
346 }
347
348
349 func TestTx_DeleteBucket(t *testing.T) {
350 db := btesting.MustCreateDB(t)
351
352
353 if err := db.Update(func(tx *bolt.Tx) error {
354 b, err := tx.CreateBucket([]byte("widgets"))
355 if err != nil {
356 t.Fatal(err)
357 }
358 if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
359 t.Fatal(err)
360 }
361 return nil
362 }); err != nil {
363 t.Fatal(err)
364 }
365
366
367 if err := db.Update(func(tx *bolt.Tx) error {
368 if err := tx.DeleteBucket([]byte("widgets")); err != nil {
369 t.Fatal(err)
370 }
371 if tx.Bucket([]byte("widgets")) != nil {
372 t.Fatal("unexpected bucket")
373 }
374 return nil
375 }); err != nil {
376 t.Fatal(err)
377 }
378
379 if err := db.Update(func(tx *bolt.Tx) error {
380
381 b, err := tx.CreateBucket([]byte("widgets"))
382 if err != nil {
383 t.Fatal(err)
384 }
385 if v := b.Get([]byte("foo")); v != nil {
386 t.Fatalf("unexpected phantom value: %v", v)
387 }
388 return nil
389 }); err != nil {
390 t.Fatal(err)
391 }
392 }
393
394
395 func TestTx_DeleteBucket_ErrTxClosed(t *testing.T) {
396 db := btesting.MustCreateDB(t)
397 tx, err := db.Begin(true)
398 if err != nil {
399 t.Fatal(err)
400 }
401 if err := tx.Commit(); err != nil {
402 t.Fatal(err)
403 }
404 if err := tx.DeleteBucket([]byte("foo")); err != bolt.ErrTxClosed {
405 t.Fatalf("unexpected error: %s", err)
406 }
407 }
408
409
410 func TestTx_DeleteBucket_ReadOnly(t *testing.T) {
411 db := btesting.MustCreateDB(t)
412 if err := db.View(func(tx *bolt.Tx) error {
413 if err := tx.DeleteBucket([]byte("foo")); err != bolt.ErrTxNotWritable {
414 t.Fatalf("unexpected error: %s", err)
415 }
416 return nil
417 }); err != nil {
418 t.Fatal(err)
419 }
420 }
421
422
423 func TestTx_DeleteBucket_NotFound(t *testing.T) {
424 db := btesting.MustCreateDB(t)
425 if err := db.Update(func(tx *bolt.Tx) error {
426 if err := tx.DeleteBucket([]byte("widgets")); err != bolt.ErrBucketNotFound {
427 t.Fatalf("unexpected error: %s", err)
428 }
429 return nil
430 }); err != nil {
431 t.Fatal(err)
432 }
433 }
434
435
436
437 func TestTx_ForEach_NoError(t *testing.T) {
438 db := btesting.MustCreateDB(t)
439 if err := db.Update(func(tx *bolt.Tx) error {
440 b, err := tx.CreateBucket([]byte("widgets"))
441 if err != nil {
442 t.Fatal(err)
443 }
444 if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
445 t.Fatal(err)
446 }
447
448 if err := tx.ForEach(func(name []byte, b *bolt.Bucket) error {
449 return nil
450 }); err != nil {
451 t.Fatal(err)
452 }
453 return nil
454 }); err != nil {
455 t.Fatal(err)
456 }
457 }
458
459
460 func TestTx_ForEach_WithError(t *testing.T) {
461 db := btesting.MustCreateDB(t)
462 if err := db.Update(func(tx *bolt.Tx) error {
463 b, err := tx.CreateBucket([]byte("widgets"))
464 if err != nil {
465 t.Fatal(err)
466 }
467 if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
468 t.Fatal(err)
469 }
470
471 marker := errors.New("marker")
472 if err := tx.ForEach(func(name []byte, b *bolt.Bucket) error {
473 return marker
474 }); err != marker {
475 t.Fatalf("unexpected error: %s", err)
476 }
477 return nil
478 }); err != nil {
479 t.Fatal(err)
480 }
481 }
482
483
484 func TestTx_OnCommit(t *testing.T) {
485 db := btesting.MustCreateDB(t)
486
487 var x int
488 if err := db.Update(func(tx *bolt.Tx) error {
489 tx.OnCommit(func() { x += 1 })
490 tx.OnCommit(func() { x += 2 })
491 if _, err := tx.CreateBucket([]byte("widgets")); err != nil {
492 t.Fatal(err)
493 }
494 return nil
495 }); err != nil {
496 t.Fatal(err)
497 } else if x != 3 {
498 t.Fatalf("unexpected x: %d", x)
499 }
500 }
501
502
503 func TestTx_OnCommit_Rollback(t *testing.T) {
504 db := btesting.MustCreateDB(t)
505
506 var x int
507 if err := db.Update(func(tx *bolt.Tx) error {
508 tx.OnCommit(func() { x += 1 })
509 tx.OnCommit(func() { x += 2 })
510 if _, err := tx.CreateBucket([]byte("widgets")); err != nil {
511 t.Fatal(err)
512 }
513 return errors.New("rollback this commit")
514 }); err == nil || err.Error() != "rollback this commit" {
515 t.Fatalf("unexpected error: %s", err)
516 } else if x != 0 {
517 t.Fatalf("unexpected x: %d", x)
518 }
519 }
520
521
522 func TestTx_CopyFile(t *testing.T) {
523 db := btesting.MustCreateDB(t)
524
525 path := tempfile()
526 if err := db.Update(func(tx *bolt.Tx) error {
527 b, err := tx.CreateBucket([]byte("widgets"))
528 if err != nil {
529 t.Fatal(err)
530 }
531 if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
532 t.Fatal(err)
533 }
534 if err := b.Put([]byte("baz"), []byte("bat")); err != nil {
535 t.Fatal(err)
536 }
537 return nil
538 }); err != nil {
539 t.Fatal(err)
540 }
541
542 if err := db.View(func(tx *bolt.Tx) error {
543 return tx.CopyFile(path, 0600)
544 }); err != nil {
545 t.Fatal(err)
546 }
547
548 db2, err := bolt.Open(path, 0600, nil)
549 if err != nil {
550 t.Fatal(err)
551 }
552
553 if err := db2.View(func(tx *bolt.Tx) error {
554 if v := tx.Bucket([]byte("widgets")).Get([]byte("foo")); !bytes.Equal(v, []byte("bar")) {
555 t.Fatalf("unexpected value: %v", v)
556 }
557 if v := tx.Bucket([]byte("widgets")).Get([]byte("baz")); !bytes.Equal(v, []byte("bat")) {
558 t.Fatalf("unexpected value: %v", v)
559 }
560 return nil
561 }); err != nil {
562 t.Fatal(err)
563 }
564
565 if err := db2.Close(); err != nil {
566 t.Fatal(err)
567 }
568 }
569
570 type failWriterError struct{}
571
572 func (failWriterError) Error() string {
573 return "error injected for tests"
574 }
575
576 type failWriter struct {
577
578 After int
579 }
580
581 func (f *failWriter) Write(p []byte) (n int, err error) {
582 n = len(p)
583 if n > f.After {
584 n = f.After
585 err = failWriterError{}
586 }
587 f.After -= n
588 return n, err
589 }
590
591
592 func TestTx_CopyFile_Error_Meta(t *testing.T) {
593 db := btesting.MustCreateDB(t)
594 if err := db.Update(func(tx *bolt.Tx) error {
595 b, err := tx.CreateBucket([]byte("widgets"))
596 if err != nil {
597 t.Fatal(err)
598 }
599 if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
600 t.Fatal(err)
601 }
602 if err := b.Put([]byte("baz"), []byte("bat")); err != nil {
603 t.Fatal(err)
604 }
605 return nil
606 }); err != nil {
607 t.Fatal(err)
608 }
609
610 if err := db.View(func(tx *bolt.Tx) error {
611 return tx.Copy(&failWriter{})
612 }); err == nil || err.Error() != "meta 0 copy: error injected for tests" {
613 t.Fatalf("unexpected error: %v", err)
614 }
615 }
616
617
618 func TestTx_CopyFile_Error_Normal(t *testing.T) {
619 db := btesting.MustCreateDB(t)
620 if err := db.Update(func(tx *bolt.Tx) error {
621 b, err := tx.CreateBucket([]byte("widgets"))
622 if err != nil {
623 t.Fatal(err)
624 }
625 if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
626 t.Fatal(err)
627 }
628 if err := b.Put([]byte("baz"), []byte("bat")); err != nil {
629 t.Fatal(err)
630 }
631 return nil
632 }); err != nil {
633 t.Fatal(err)
634 }
635
636 if err := db.View(func(tx *bolt.Tx) error {
637 return tx.Copy(&failWriter{3 * db.Info().PageSize})
638 }); err == nil || err.Error() != "error injected for tests" {
639 t.Fatalf("unexpected error: %v", err)
640 }
641 }
642
643
644 func TestTx_Rollback(t *testing.T) {
645 for _, isSyncFreelist := range []bool{false, true} {
646
647 db, err := bolt.Open(tempfile(), 0666, nil)
648 if err != nil {
649 log.Fatal(err)
650 }
651 defer os.Remove(db.Path())
652 db.NoFreelistSync = isSyncFreelist
653
654 tx, err := db.Begin(true)
655 if err != nil {
656 t.Fatalf("Error starting tx: %v", err)
657 }
658 bucket := []byte("mybucket")
659 if _, err := tx.CreateBucket(bucket); err != nil {
660 t.Fatalf("Error creating bucket: %v", err)
661 }
662 if err := tx.Commit(); err != nil {
663 t.Fatalf("Error on commit: %v", err)
664 }
665
666 tx, err = db.Begin(true)
667 if err != nil {
668 t.Fatalf("Error starting tx: %v", err)
669 }
670 b := tx.Bucket(bucket)
671 if err := b.Put([]byte("k"), []byte("v")); err != nil {
672 t.Fatalf("Error on put: %v", err)
673 }
674
675 if err := tx.Rollback(); err != nil {
676 t.Fatalf("Error on rollback: %v", err)
677 }
678
679 tx, err = db.Begin(false)
680 if err != nil {
681 t.Fatalf("Error starting tx: %v", err)
682 }
683 b = tx.Bucket(bucket)
684 if v := b.Get([]byte("k")); v != nil {
685 t.Fatalf("Value for k should not have been stored")
686 }
687 if err := tx.Rollback(); err != nil {
688 t.Fatalf("Error on rollback: %v", err)
689 }
690
691 }
692 }
693
694
695
696
697
698
699 func TestTx_releaseRange(t *testing.T) {
700
701
702
703 db := btesting.MustCreateDBWithOption(t, &bolt.Options{InitialMmapSize: os.Getpagesize() * 100})
704
705 bucket := "bucket"
706
707 put := func(key, value string) {
708 if err := db.Update(func(tx *bolt.Tx) error {
709 b, err := tx.CreateBucketIfNotExists([]byte(bucket))
710 if err != nil {
711 t.Fatal(err)
712 }
713 return b.Put([]byte(key), []byte(value))
714 }); err != nil {
715 t.Fatal(err)
716 }
717 }
718
719 del := func(key string) {
720 if err := db.Update(func(tx *bolt.Tx) error {
721 b, err := tx.CreateBucketIfNotExists([]byte(bucket))
722 if err != nil {
723 t.Fatal(err)
724 }
725 return b.Delete([]byte(key))
726 }); err != nil {
727 t.Fatal(err)
728 }
729 }
730
731 getWithTxn := func(txn *bolt.Tx, key string) []byte {
732 return txn.Bucket([]byte(bucket)).Get([]byte(key))
733 }
734
735 openReadTxn := func() *bolt.Tx {
736 readTx, err := db.Begin(false)
737 if err != nil {
738 t.Fatal(err)
739 }
740 return readTx
741 }
742
743 checkWithReadTxn := func(txn *bolt.Tx, key string, wantValue []byte) {
744 value := getWithTxn(txn, key)
745 if !bytes.Equal(value, wantValue) {
746 t.Errorf("Wanted value to be %s for key %s, but got %s", wantValue, key, string(value))
747 }
748 }
749
750 rollback := func(txn *bolt.Tx) {
751 if err := txn.Rollback(); err != nil {
752 t.Fatal(err)
753 }
754 }
755
756 put("k1", "v1")
757 rtx1 := openReadTxn()
758 put("k2", "v2")
759 hold1 := openReadTxn()
760 put("k3", "v3")
761 hold2 := openReadTxn()
762 del("k3")
763 rtx2 := openReadTxn()
764 del("k1")
765 hold3 := openReadTxn()
766 del("k2")
767 hold4 := openReadTxn()
768 put("k4", "v4")
769 hold5 := openReadTxn()
770
771
772 rollback(hold1)
773 rollback(hold2)
774 rollback(hold3)
775 rollback(hold4)
776 rollback(hold5)
777
778
779
780
781 put("k4", "v4")
782
783
784 checkWithReadTxn(rtx1, "k1", []byte("v1"))
785 checkWithReadTxn(rtx2, "k2", []byte("v2"))
786 rollback(rtx1)
787 rollback(rtx2)
788
789
790 rtx7 := openReadTxn()
791 checkWithReadTxn(rtx7, "k1", nil)
792 checkWithReadTxn(rtx7, "k2", nil)
793 checkWithReadTxn(rtx7, "k3", nil)
794 checkWithReadTxn(rtx7, "k4", []byte("v4"))
795 rollback(rtx7)
796 }
797
798 func ExampleTx_Rollback() {
799
800 db, err := bolt.Open(tempfile(), 0666, nil)
801 if err != nil {
802 log.Fatal(err)
803 }
804 defer os.Remove(db.Path())
805
806
807 if err := db.Update(func(tx *bolt.Tx) error {
808 _, err := tx.CreateBucket([]byte("widgets"))
809 return err
810 }); err != nil {
811 log.Fatal(err)
812 }
813
814
815 if err := db.Update(func(tx *bolt.Tx) error {
816 return tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar"))
817 }); err != nil {
818 log.Fatal(err)
819 }
820
821
822 tx, err := db.Begin(true)
823 if err != nil {
824 log.Fatal(err)
825 }
826 b := tx.Bucket([]byte("widgets"))
827 if err := b.Put([]byte("foo"), []byte("baz")); err != nil {
828 log.Fatal(err)
829 }
830 if err := tx.Rollback(); err != nil {
831 log.Fatal(err)
832 }
833
834
835 if err := db.View(func(tx *bolt.Tx) error {
836 value := tx.Bucket([]byte("widgets")).Get([]byte("foo"))
837 fmt.Printf("The value for 'foo' is still: %s\n", value)
838 return nil
839 }); err != nil {
840 log.Fatal(err)
841 }
842
843
844 if err := db.Close(); err != nil {
845 log.Fatal(err)
846 }
847
848
849
850 }
851
852 func ExampleTx_CopyFile() {
853
854 db, err := bolt.Open(tempfile(), 0666, nil)
855 if err != nil {
856 log.Fatal(err)
857 }
858 defer os.Remove(db.Path())
859
860
861 if err := db.Update(func(tx *bolt.Tx) error {
862 b, err := tx.CreateBucket([]byte("widgets"))
863 if err != nil {
864 return err
865 }
866 if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
867 return err
868 }
869 return nil
870 }); err != nil {
871 log.Fatal(err)
872 }
873
874
875 toFile := tempfile()
876 if err := db.View(func(tx *bolt.Tx) error {
877 return tx.CopyFile(toFile, 0666)
878 }); err != nil {
879 log.Fatal(err)
880 }
881 defer os.Remove(toFile)
882
883
884 db2, err := bolt.Open(toFile, 0666, nil)
885 if err != nil {
886 log.Fatal(err)
887 }
888
889
890 if err := db2.View(func(tx *bolt.Tx) error {
891 value := tx.Bucket([]byte("widgets")).Get([]byte("foo"))
892 fmt.Printf("The value for 'foo' in the clone is: %s\n", value)
893 return nil
894 }); err != nil {
895 log.Fatal(err)
896 }
897
898
899 if err := db.Close(); err != nil {
900 log.Fatal(err)
901 }
902
903 if err := db2.Close(); err != nil {
904 log.Fatal(err)
905 }
906
907
908
909 }
910
911 func TestTxStats_GetAndIncAtomically(t *testing.T) {
912 var stats bolt.TxStats
913
914 stats.IncPageCount(1)
915 assert.Equal(t, int64(1), stats.GetPageCount())
916
917 stats.IncPageAlloc(2)
918 assert.Equal(t, int64(2), stats.GetPageAlloc())
919
920 stats.IncCursorCount(3)
921 assert.Equal(t, int64(3), stats.GetCursorCount())
922
923 stats.IncNodeCount(100)
924 assert.Equal(t, int64(100), stats.GetNodeCount())
925
926 stats.IncNodeDeref(101)
927 assert.Equal(t, int64(101), stats.GetNodeDeref())
928
929 stats.IncRebalance(1000)
930 assert.Equal(t, int64(1000), stats.GetRebalance())
931
932 stats.IncRebalanceTime(1001 * time.Second)
933 assert.Equal(t, 1001*time.Second, stats.GetRebalanceTime())
934
935 stats.IncSplit(10000)
936 assert.Equal(t, int64(10000), stats.GetSplit())
937
938 stats.IncSpill(10001)
939 assert.Equal(t, int64(10001), stats.GetSpill())
940
941 stats.IncSpillTime(10001 * time.Second)
942 assert.Equal(t, 10001*time.Second, stats.GetSpillTime())
943
944 stats.IncWrite(100000)
945 assert.Equal(t, int64(100000), stats.GetWrite())
946
947 stats.IncWriteTime(100001 * time.Second)
948 assert.Equal(t, 100001*time.Second, stats.GetWriteTime())
949
950 assert.Equal(t,
951 bolt.TxStats{
952 PageCount: 1,
953 PageAlloc: 2,
954 CursorCount: 3,
955 NodeCount: 100,
956 NodeDeref: 101,
957 Rebalance: 1000,
958 RebalanceTime: 1001 * time.Second,
959 Split: 10000,
960 Spill: 10001,
961 SpillTime: 10001 * time.Second,
962 Write: 100000,
963 WriteTime: 100001 * time.Second,
964 },
965 stats,
966 )
967 }
968
969 func TestTxStats_Sub(t *testing.T) {
970 statsA := bolt.TxStats{
971 PageCount: 1,
972 PageAlloc: 2,
973 CursorCount: 3,
974 NodeCount: 100,
975 NodeDeref: 101,
976 Rebalance: 1000,
977 RebalanceTime: 1001 * time.Second,
978 Split: 10000,
979 Spill: 10001,
980 SpillTime: 10001 * time.Second,
981 Write: 100000,
982 WriteTime: 100001 * time.Second,
983 }
984
985 statsB := bolt.TxStats{
986 PageCount: 2,
987 PageAlloc: 3,
988 CursorCount: 4,
989 NodeCount: 101,
990 NodeDeref: 102,
991 Rebalance: 1001,
992 RebalanceTime: 1002 * time.Second,
993 Split: 11001,
994 Spill: 11002,
995 SpillTime: 11002 * time.Second,
996 Write: 110001,
997 WriteTime: 110010 * time.Second,
998 }
999
1000 diff := statsB.Sub(&statsA)
1001 assert.Equal(t, int64(1), diff.GetPageCount())
1002 assert.Equal(t, int64(1), diff.GetPageAlloc())
1003 assert.Equal(t, int64(1), diff.GetCursorCount())
1004 assert.Equal(t, int64(1), diff.GetNodeCount())
1005 assert.Equal(t, int64(1), diff.GetNodeDeref())
1006 assert.Equal(t, int64(1), diff.GetRebalance())
1007 assert.Equal(t, time.Second, diff.GetRebalanceTime())
1008 assert.Equal(t, int64(1001), diff.GetSplit())
1009 assert.Equal(t, int64(1001), diff.GetSpill())
1010 assert.Equal(t, 1001*time.Second, diff.GetSpillTime())
1011 assert.Equal(t, int64(10001), diff.GetWrite())
1012 assert.Equal(t, 10009*time.Second, diff.GetWriteTime())
1013 }
1014
1015
1016 func TestTx_TruncateBeforeWrite(t *testing.T) {
1017 if runtime.GOOS == "windows" {
1018 return
1019 }
1020 for _, isSyncFreelist := range []bool{false, true} {
1021 t.Run(fmt.Sprintf("isSyncFreelist:%v", isSyncFreelist), func(t *testing.T) {
1022
1023 db := btesting.MustCreateDBWithOption(t, &bolt.Options{
1024 NoFreelistSync: isSyncFreelist,
1025 })
1026
1027 bigvalue := make([]byte, db.AllocSize/100)
1028 count := 0
1029 for {
1030 count++
1031 tx, err := db.Begin(true)
1032 require.NoError(t, err)
1033 b, err := tx.CreateBucketIfNotExists([]byte("bucket"))
1034 require.NoError(t, err)
1035 err = b.Put([]byte{byte(count)}, bigvalue)
1036 require.NoError(t, err)
1037 err = tx.Commit()
1038 require.NoError(t, err)
1039
1040 size := fileSize(db.Path())
1041
1042 if size > int64(db.AllocSize) && size < int64(db.AllocSize)*2 {
1043
1044
1045 t.Fatalf("db.grow doesn't run when file size changes. file size: %d", size)
1046 }
1047 if size > int64(db.AllocSize) {
1048 break
1049 }
1050 }
1051 db.MustClose()
1052 db.MustDeleteFile()
1053 })
1054 }
1055 }
1056
View as plain text