func ExampleTx_CopyFile() { // Open the database. db, _ := bolt.Open(tempfile(), 0666, nil) defer os.Remove(db.Path()) defer db.Close() // Create a bucket and a key. db.Update(func(tx *bolt.Tx) error { tx.CreateBucket([]byte("widgets")) tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")) return nil }) // Copy the database to another file. toFile := tempfile() db.View(func(tx *bolt.Tx) error { return tx.CopyFile(toFile, 0666) }) defer os.Remove(toFile) // Open the cloned database. db2, _ := bolt.Open(toFile, 0666, nil) defer db2.Close() // Ensure that the key exists in the copy. db2.View(func(tx *bolt.Tx) error { value := tx.Bucket([]byte("widgets")).Get([]byte("foo")) fmt.Printf("The value for 'foo' in the clone is: %s\n", value) return nil }) // Output: // The value for 'foo' in the clone is: bar }
// Ensure that a re-opened database is consistent. func TestOpen_Check(t *testing.T) { path := tempfile() defer os.Remove(path) db, err := bolt.Open(path, 0666, nil) ok(t, err) ok(t, db.View(func(tx *bolt.Tx) error { return <-tx.Check() })) db.Close() db, err = bolt.Open(path, 0666, nil) ok(t, err) ok(t, db.View(func(tx *bolt.Tx) error { return <-tx.Check() })) db.Close() }
// Ensure that a database that is too small returns an error. func TestDB_Open_FileTooSmall(t *testing.T) { path := tempfile() defer os.Remove(path) db, err := bolt.Open(path, 0666, nil) ok(t, err) db.Close() // corrupt the database ok(t, os.Truncate(path, int64(os.Getpagesize()))) db, err = bolt.Open(path, 0666, nil) equals(t, errors.New("file size too small"), err) }
func NewBoltStore(path string) (*BoltStore, error) { db, err := bolt.Open(path, 0600, nil) if err != nil { return nil, err } db.Update(func(tx *bolt.Tx) error { tx.CreateBucketIfNotExists(boltBucketUsers) tx.CreateBucketIfNotExists(boltBucketUsernameIndex) tx.CreateBucketIfNotExists(boltBucketTokenIndex) tx.CreateBucketIfNotExists(boltBucketSettings) tx.CreateBucketIfNotExists(boltBucketSettingUserIDIndex) tx.CreateBucketIfNotExists(boltBucketCasts) tx.CreateBucketIfNotExists(boltBucketCastURLIndex) tx.CreateBucketIfNotExists(boltBucketEpisodes) tx.CreateBucketIfNotExists(boltBucketEpisodeCastIDIndex) tx.CreateBucketIfNotExists(boltBucketEpisodeGUIDIndex) tx.CreateBucketIfNotExists(boltBucketEpisodeCrawlTSIndex) tx.CreateBucketIfNotExists(boltBucketEvents) tx.CreateBucketIfNotExists(boltBucketLabels) tx.CreateBucketIfNotExists(boltBucketLabelUserIDIndex) tx.CreateBucketIfNotExists(boltBucketLabelNameIndex) tx.CreateBucketIfNotExists(boltBucketLabelRootIndex) return nil }) return &BoltStore{ db: db, }, nil }
// Keys retrieves a list of keys for a given bucket. func Keys(path, name string) { if _, err := os.Stat(path); os.IsNotExist(err) { fatal(err) return } db, err := bolt.Open(path, 0600, nil) if err != nil { fatal(err) return } defer db.Close() err = db.View(func(tx *bolt.Tx) error { // Find bucket. b := tx.Bucket([]byte(name)) if b == nil { fatalf("bucket not found: %s", name) return nil } // Iterate over each key. return b.ForEach(func(key, _ []byte) error { println(string(key)) return nil }) }) if err != nil { fatal(err) return } }
func ExampleTx_Rollback() { // Open the database. db, _ := bolt.Open(tempfile(), 0666, nil) defer os.Remove(db.Path()) defer db.Close() // Create a bucket. db.Update(func(tx *bolt.Tx) error { _, err := tx.CreateBucket([]byte("widgets")) return err }) // Set a value for a key. db.Update(func(tx *bolt.Tx) error { return tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")) }) // Update the key but rollback the transaction so it never saves. tx, _ := db.Begin(true) b := tx.Bucket([]byte("widgets")) b.Put([]byte("foo"), []byte("baz")) tx.Rollback() // Ensure that our original value is still set. db.View(func(tx *bolt.Tx) error { value := tx.Bucket([]byte("widgets")).Get([]byte("foo")) fmt.Printf("The value for 'foo' is still: %s\n", value) return nil }) // Output: // The value for 'foo' is still: bar }
// NewTestDB returns a new instance of TestDB. func NewTestDB() *TestDB { db, err := bolt.Open(tempfile(), 0666, nil) if err != nil { panic("cannot open db: " + err.Error()) } return &TestDB{db} }
func ExampleDB_Begin_ReadOnly() { // Open the database. db, _ := bolt.Open(tempfile(), 0666, nil) defer os.Remove(db.Path()) defer db.Close() // Create a bucket. db.Update(func(tx *bolt.Tx) error { _, err := tx.CreateBucket([]byte("widgets")) return err }) // Create several keys in a transaction. tx, _ := db.Begin(true) b := tx.Bucket([]byte("widgets")) b.Put([]byte("john"), []byte("blue")) b.Put([]byte("abby"), []byte("red")) b.Put([]byte("zephyr"), []byte("purple")) tx.Commit() // Iterate over the values in sorted key order. tx, _ = db.Begin(false) c := tx.Bucket([]byte("widgets")).Cursor() for k, v := c.First(); k != nil; k, v = c.Next() { fmt.Printf("%s likes %s\n", k, v) } tx.Rollback() // Output: // abby likes red // john likes blue // zephyr likes purple }
func ExampleDB_View() { // Open the database. db, _ := bolt.Open(tempfile(), 0666, nil) defer os.Remove(db.Path()) defer db.Close() // Insert data into a bucket. db.Update(func(tx *bolt.Tx) error { tx.CreateBucket([]byte("people")) b := tx.Bucket([]byte("people")) b.Put([]byte("john"), []byte("doe")) b.Put([]byte("susy"), []byte("que")) return nil }) // Access data from within a read-only transactional block. db.View(func(tx *bolt.Tx) error { v := tx.Bucket([]byte("people")).Get([]byte("john")) fmt.Printf("John's last name is %s.\n", v) return nil }) // Output: // John's last name is doe. }
func ExampleDB_Update() { // Open the database. db, _ := bolt.Open(tempfile(), 0666, nil) defer os.Remove(db.Path()) defer db.Close() // Execute several commands within a write transaction. err := db.Update(func(tx *bolt.Tx) error { b, err := tx.CreateBucket([]byte("widgets")) if err != nil { return err } if err := b.Put([]byte("foo"), []byte("bar")); err != nil { return err } return nil }) // If our transactional block didn't return an error then our data is saved. if err == nil { db.View(func(tx *bolt.Tx) error { value := tx.Bucket([]byte("widgets")).Get([]byte("foo")) fmt.Printf("The value of 'foo' is: %s\n", value) return nil }) } // Output: // The value of 'foo' is: bar }
func ExampleBucket_ForEach() { // Open the database. db, _ := bolt.Open(tempfile(), 0666, nil) defer os.Remove(db.Path()) defer db.Close() // Insert data into a bucket. db.Update(func(tx *bolt.Tx) error { tx.CreateBucket([]byte("animals")) b := tx.Bucket([]byte("animals")) b.Put([]byte("dog"), []byte("fun")) b.Put([]byte("cat"), []byte("lame")) b.Put([]byte("liger"), []byte("awesome")) // Iterate over items in sorted key order. b.ForEach(func(k, v []byte) error { fmt.Printf("A %s is %s.\n", k, v) return nil }) return nil }) // Output: // A cat is lame. // A dog is fun. // A liger is awesome. }
func ExampleBucket_Put() { // Open the database. db, _ := bolt.Open(tempfile(), 0666, nil) defer os.Remove(db.Path()) defer db.Close() // Start a write transaction. db.Update(func(tx *bolt.Tx) error { // Create a bucket. tx.CreateBucket([]byte("widgets")) // Set the value "bar" for the key "foo". tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")) return nil }) // Read value back in a different read-only transaction. db.View(func(tx *bolt.Tx) error { value := tx.Bucket([]byte("widgets")).Get([]byte("foo")) fmt.Printf("The value of 'foo' is: %s\n", value) return nil }) // Output: // The value of 'foo' is: bar }
// Ensure that a database can be opened without error. func TestOpen(t *testing.T) { path := tempfile() defer os.Remove(path) db, err := bolt.Open(path, 0666, nil) assert(t, db != nil, "") ok(t, err) equals(t, db.Path(), path) ok(t, db.Close()) }
// Ensure that the database returns an error if the file handle cannot be open. func TestDB_Open_FileError(t *testing.T) { path := tempfile() defer os.Remove(path) _, err := bolt.Open(path+"/youre-not-my-real-parent", 0666, nil) assert(t, err.(*os.PathError) != nil, "") equals(t, path+"/youre-not-my-real-parent", err.(*os.PathError).Path) equals(t, "open", err.(*os.PathError).Op) }
// open creates and opens a Bolt database in the temp directory. func open(fn func(*bolt.DB, string)) { path := tempfile() defer os.RemoveAll(path) db, err := bolt.Open(path, 0600, nil) if err != nil { panic("db open error: " + err.Error()) } fn(db, path) }
// Ensure that opening an already open database file will timeout. func TestOpen_Timeout(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("timeout not supported on windows") } path := tempfile() defer os.Remove(path) // Open a data file. db0, err := bolt.Open(path, 0666, nil) assert(t, db0 != nil, "") ok(t, err) // Attempt to open the database again. start := time.Now() db1, err := bolt.Open(path, 0666, &bolt.Options{Timeout: 100 * time.Millisecond}) assert(t, db1 == nil, "") equals(t, bolt.ErrTimeout, err) assert(t, time.Since(start) > 100*time.Millisecond, "") db0.Close() }
// Ensure that opening a database beyond the max step size does not increase its size. // https://github.com/boltdb/bolt/issues/303 func TestOpen_Size_Large(t *testing.T) { if testing.Short() { t.Skip("short mode") } // Open a data file. db := NewTestDB() path := db.Path() defer db.Close() // Insert until we get above the minimum 4MB size. var index uint64 for i := 0; i < 10000; i++ { ok(t, db.Update(func(tx *bolt.Tx) error { b, _ := tx.CreateBucketIfNotExists([]byte("data")) for j := 0; j < 1000; j++ { ok(t, b.Put(u64tob(index), make([]byte, 50))) index++ } return nil })) } // Close database and grab the size. db.DB.Close() sz := fileSize(path) if sz == 0 { t.Fatalf("unexpected new file size: %d", sz) } else if sz < (1 << 30) { t.Fatalf("expected larger initial size: %d", sz) } // Reopen database, update, and check size again. db0, err := bolt.Open(path, 0666, nil) ok(t, err) ok(t, db0.Update(func(tx *bolt.Tx) error { return tx.Bucket([]byte("data")).Put([]byte{0}, []byte{0}) })) ok(t, db0.Close()) newSz := fileSize(path) if newSz == 0 { t.Fatalf("unexpected new file size: %d", newSz) } // Compare the original size with the new size. if sz != newSz { t.Fatalf("unexpected file growth: %d => %d", sz, newSz) } }
// Pages prints a list of all pages in a database. func Pages(path string) { if _, err := os.Stat(path); os.IsNotExist(err) { fatal(err) return } db, err := bolt.Open(path, 0600, nil) if err != nil { fatal(err) return } defer db.Close() println("ID TYPE ITEMS OVRFLW") println("======== ========== ====== ======") db.Update(func(tx *bolt.Tx) error { var id int for { p, err := tx.Page(id) if err != nil { fatalf("page error: %d: %s", id, err) } else if p == nil { break } // Only display count and overflow if this is a non-free page. var count, overflow string if p.Type != "free" { count = strconv.Itoa(p.Count) if p.OverflowCount > 0 { overflow = strconv.Itoa(p.OverflowCount) } } // Print table row. printf("%-8d %-10s %-6s %-6s\n", p.ID, p.Type, count, overflow) // Move to the next non-overflow page. id += 1 if p.Type != "free" { id += p.OverflowCount } } return nil }) }
// Info prints basic information about a database. func Info(path string) { if _, err := os.Stat(path); os.IsNotExist(err) { fatal(err) return } db, err := bolt.Open(path, 0600, nil) if err != nil { fatal(err) return } defer db.Close() // Print basic database info. var info = db.Info() printf("Page Size: %d\n", info.PageSize) }
func ExampleBucket_Delete() { // Open the database. db, _ := bolt.Open(tempfile(), 0666, nil) defer os.Remove(db.Path()) defer db.Close() // Start a write transaction. db.Update(func(tx *bolt.Tx) error { // Create a bucket. tx.CreateBucket([]byte("widgets")) b := tx.Bucket([]byte("widgets")) // Set the value "bar" for the key "foo". b.Put([]byte("foo"), []byte("bar")) // Retrieve the key back from the database and verify it. value := b.Get([]byte("foo")) fmt.Printf("The value of 'foo' was: %s\n", value) return nil }) // Delete the key in a different write transaction. db.Update(func(tx *bolt.Tx) error { return tx.Bucket([]byte("widgets")).Delete([]byte("foo")) }) // Retrieve the key again. db.View(func(tx *bolt.Tx) error { value := tx.Bucket([]byte("widgets")).Get([]byte("foo")) if value == nil { fmt.Printf("The value of 'foo' is now: nil\n") } return nil }) // Output: // The value of 'foo' was: bar // The value of 'foo' is now: nil }
// Check performs a consistency check on the database and prints any errors found. func Check(path string) { if _, err := os.Stat(path); os.IsNotExist(err) { fatal(err) return } db, err := bolt.Open(path, 0600, nil) if err != nil { fatal(err) return } defer db.Close() // Perform consistency check. _ = db.View(func(tx *bolt.Tx) error { var count int ch := tx.Check() loop: for { select { case err, ok := <-ch: if !ok { break loop } println(err) count++ } } // Print summary of errors. if count > 0 { fatalf("%d errors found", count) } else { println("OK") } return nil }) }
func ExampleCursor_reverse() { // Open the database. db, _ := bolt.Open(tempfile(), 0666, nil) defer os.Remove(db.Path()) defer db.Close() // Start a read-write transaction. db.Update(func(tx *bolt.Tx) error { // Create a new bucket. tx.CreateBucket([]byte("animals")) // Insert data into a bucket. b := tx.Bucket([]byte("animals")) b.Put([]byte("dog"), []byte("fun")) b.Put([]byte("cat"), []byte("lame")) b.Put([]byte("liger"), []byte("awesome")) // Create a cursor for iteration. c := b.Cursor() // Iterate over items in reverse sorted key order. This starts // from the last key/value pair and updates the k/v variables to // the previous key/value on each iteration. // // The loop finishes at the beginning of the cursor when a nil key // is returned. for k, v := c.Last(); k != nil; k, v = c.Prev() { fmt.Printf("A %s is %s.\n", k, v) } return nil }) // Output: // A liger is awesome. // A dog is fun. // A cat is lame. }
// Ensure that the database can be copied to a file path. func TestTx_CopyFile(t *testing.T) { db := NewTestDB() defer db.Close() var dest = tempfile() db.Update(func(tx *bolt.Tx) error { tx.CreateBucket([]byte("widgets")) tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")) tx.Bucket([]byte("widgets")).Put([]byte("baz"), []byte("bat")) return nil }) ok(t, db.View(func(tx *bolt.Tx) error { return tx.CopyFile(dest, 0600) })) db2, err := bolt.Open(dest, 0600, nil) ok(t, err) defer db2.Close() db2.View(func(tx *bolt.Tx) error { equals(t, []byte("bar"), tx.Bucket([]byte("widgets")).Get([]byte("foo"))) equals(t, []byte("bat"), tx.Bucket([]byte("widgets")).Get([]byte("baz"))) return nil }) }
// Ensure that opening a database does not increase its size. // https://github.com/boltdb/bolt/issues/291 func TestOpen_Size(t *testing.T) { // Open a data file. db := NewTestDB() path := db.Path() defer db.Close() // Insert until we get above the minimum 4MB size. ok(t, db.Update(func(tx *bolt.Tx) error { b, _ := tx.CreateBucketIfNotExists([]byte("data")) for i := 0; i < 10000; i++ { ok(t, b.Put([]byte(fmt.Sprintf("%04d", i)), make([]byte, 1000))) } return nil })) // Close database and grab the size. db.DB.Close() sz := fileSize(path) if sz == 0 { t.Fatalf("unexpected new file size: %d", sz) } // Reopen database, update, and check size again. db0, err := bolt.Open(path, 0666, nil) ok(t, err) ok(t, db0.Update(func(tx *bolt.Tx) error { return tx.Bucket([]byte("data")).Put([]byte{0}, []byte{0}) })) ok(t, db0.Close()) newSz := fileSize(path) if newSz == 0 { t.Fatalf("unexpected new file size: %d", newSz) } // Compare the original size with the new size. if sz != newSz { t.Fatalf("unexpected file growth: %d => %d", sz, newSz) } }
// Get retrieves the value for a given bucket/key. func Get(path, name, key string) { if _, err := os.Stat(path); os.IsNotExist(err) { fatal(err) return } db, err := bolt.Open(path, 0600, nil) if err != nil { fatal(err) return } defer db.Close() err = db.View(func(tx *bolt.Tx) error { // Find bucket. b := tx.Bucket([]byte(name)) if b == nil { fatalf("bucket not found: %s", name) return nil } // Find value for a given key. value := b.Get([]byte(key)) if value == nil { fatalf("key not found: %s", key) return nil } println(string(value)) return nil }) if err != nil { fatal(err) return } }
// Buckets prints a list of all buckets. func Buckets(path string) { if _, err := os.Stat(path); os.IsNotExist(err) { fatal(err) return } db, err := bolt.Open(path, 0600, nil) if err != nil { fatal(err) return } defer db.Close() err = db.View(func(tx *bolt.Tx) error { return tx.ForEach(func(name []byte, _ *bolt.Bucket) error { println(string(name)) return nil }) }) if err != nil { fatal(err) return } }
// Ensure that opening a database with a bad path returns an error. func TestOpen_BadPath(t *testing.T) { db, err := bolt.Open("", 0666, nil) assert(t, err != nil, "err: %s", err) assert(t, db == nil, "") }
// Bench executes a customizable, synthetic benchmark against Bolt. func Bench(options *BenchOptions) { var results BenchResults // Validate options. if options.BatchSize == 0 { options.BatchSize = options.Iterations } else if options.Iterations%options.BatchSize != 0 { fatal("number of iterations must be divisible by the batch size") } // Find temporary location. path := tempfile() if options.Clean { defer os.Remove(path) } else { println("work:", path) } // Create database. db, err := bolt.Open(path, 0600, nil) if err != nil { fatal(err) return } db.NoSync = options.NoSync defer db.Close() // Enable streaming stats. if options.StatsInterval > 0 { go printStats(db, options.StatsInterval) } // Start profiling for writes. if options.ProfileMode == "rw" || options.ProfileMode == "w" { benchStartProfiling(options) } // Write to the database. if err := benchWrite(db, options, &results); err != nil { fatal("bench: write: ", err) } // Stop profiling for writes only. if options.ProfileMode == "w" { benchStopProfiling() } // Start profiling for reads. if options.ProfileMode == "r" { benchStartProfiling(options) } // Read from the database. if err := benchRead(db, options, &results); err != nil { fatal("bench: read: ", err) } // Stop profiling for writes only. if options.ProfileMode == "rw" || options.ProfileMode == "r" { benchStopProfiling() } // Print results. fmt.Fprintf(os.Stderr, "# Write\t%v\t(%v/op)\t(%v op/sec)\n", results.WriteDuration, results.WriteOpDuration(), results.WriteOpsPerSecond()) fmt.Fprintf(os.Stderr, "# Read\t%v\t(%v/op)\t(%v op/sec)\n", results.ReadDuration, results.ReadOpDuration(), results.ReadOpsPerSecond()) fmt.Fprintln(os.Stderr, "") }
// Collect stats for all top level buckets matching the prefix. func Stats(path, prefix string) { if _, err := os.Stat(path); os.IsNotExist(err) { fatal(err) return } db, err := bolt.Open(path, 0600, nil) if err != nil { fatal(err) return } defer db.Close() err = db.View(func(tx *bolt.Tx) error { var s bolt.BucketStats var count int var prefix = []byte(prefix) tx.ForEach(func(name []byte, b *bolt.Bucket) error { if bytes.HasPrefix(name, prefix) { s.Add(b.Stats()) count += 1 } return nil }) printf("Aggregate statistics for %d buckets\n\n", count) println("Page count statistics") printf("\tNumber of logical branch pages: %d\n", s.BranchPageN) printf("\tNumber of physical branch overflow pages: %d\n", s.BranchOverflowN) printf("\tNumber of logical leaf pages: %d\n", s.LeafPageN) printf("\tNumber of physical leaf overflow pages: %d\n", s.LeafOverflowN) println("Tree statistics") printf("\tNumber of keys/value pairs: %d\n", s.KeyN) printf("\tNumber of levels in B+tree: %d\n", s.Depth) println("Page size utilization") printf("\tBytes allocated for physical branch pages: %d\n", s.BranchAlloc) var percentage int if s.BranchAlloc != 0 { percentage = int(float32(s.BranchInuse) * 100.0 / float32(s.BranchAlloc)) } printf("\tBytes actually used for branch data: %d (%d%%)\n", s.BranchInuse, percentage) printf("\tBytes allocated for physical leaf pages: %d\n", s.LeafAlloc) percentage = 0 if s.LeafAlloc != 0 { percentage = int(float32(s.LeafInuse) * 100.0 / float32(s.LeafAlloc)) } printf("\tBytes actually used for leaf data: %d (%d%%)\n", s.LeafInuse, percentage) println("Bucket statistics") printf("\tTotal number of buckets: %d\n", s.BucketN) percentage = int(float32(s.InlineBucketN) * 100.0 / float32(s.BucketN)) printf("\tTotal number on inlined buckets: %d (%d%%)\n", s.InlineBucketN, percentage) percentage = 0 if s.LeafInuse != 0 { percentage = int(float32(s.InlineBucketInuse) * 100.0 / float32(s.LeafInuse)) } printf("\tBytes used for inlined buckets: %d (%d%%)\n", s.InlineBucketInuse, percentage) return nil }) if err != nil { fatal(err) return } }