diff --git a/bucket.go b/bucket.go index 5a4d7cb43..157d546e6 100644 --- a/bucket.go +++ b/bucket.go @@ -205,6 +205,16 @@ func (b *Bucket) CreateBucketIfNotExists(key []byte) (*Bucket, error) { return b.CreateBucket(key) } +func (b *Bucket) CreateBucketIfNotExistsOld(key []byte) (*Bucket, error) { + child, err := b.CreateBucket(key) + if err == errors.ErrBucketExists { + return b.Bucket(key), nil + } else if err != nil { + return nil, err + } + return child, nil +} + // DeleteBucket deletes a bucket at the given key. // Returns an error if the bucket does not exist, or if the key represents a non-bucket value. func (b *Bucket) DeleteBucket(key []byte) error { diff --git a/bucket_test.go b/bucket_test.go index 74d85e429..2fd7abcf1 100644 --- a/bucket_test.go +++ b/bucket_test.go @@ -8,6 +8,7 @@ import ( "log" "math/rand" "os" + "path/filepath" "strconv" "strings" "testing" @@ -2032,3 +2033,88 @@ func ExampleBucket_ForEach() { // A dog is fun. // A liger is awesome. } + +func BenchmarkCreateBucketIfNotExists(b *testing.B) { + benchmarkCreateBucketIfNotExists(b, func(tx *bolt.Tx) error { + _, err := tx.CreateBucketIfNotExists([]byte("my-bucket")) + return err + }) +} +func BenchmarkCreateBucketIfNotExistsOld(b *testing.B) { + benchmarkCreateBucketIfNotExists(b, func(tx *bolt.Tx) error { + _, err := tx.CreateBucketIfNotExistsOld([]byte("my-bucket")) + return err + }) +} + +func benchmarkCreateBucketIfNotExists(b *testing.B, f func(tx *bolt.Tx) error) { + dbPath := filepath.Join(os.TempDir(), "db") + // fmt.Printf("database path: %s\n", dbPath) + + fresh := false + _, err := os.Stat(dbPath) + switch { + case os.IsNotExist(err): + fresh = true + // fmt.Println("database does not exist, will create one") + case err == nil: + // fmt.Println("using existing database") + default: + b.Fatal(err) + } + + db, err := bolt.Open(dbPath, 0666, nil) + require.NoError(b, err) + defer db.Close() + + if fresh { + err = db.Update(func(tx *bolt.Tx) error { + for i := 0; i < 1000000; i++ { + if i%1000 == 0 { + // fmt.Printf("created %d buckets\n", i+1) + } + _, err := tx.CreateBucket([]byte("my-bucket" + strconv.Itoa(i))) + require.NoError(b, err) + } + return nil + }) + require.NoError(b, err) + } + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + err = db.Update(f) + require.NoError(b, err) + } +} + +// func BenchmarkCreateBucketIfNotExistsOld(b *testing.B) { +// f := filepath.Join(b.TempDir(), "db") +// db, err := bolt.Open(f, 0666, nil) +// require.NoError(b, err) +// defer db.Close() + +// err = db.Update(func(tx *bolt.Tx) error { +// for i := 0; i < 1000000; i++ { +// if i%1000 == 0 { +// fmt.Printf("created %d buckets\n", i) +// } +// _, err := tx.CreateBucket([]byte("my-bucket" + strconv.Itoa(i))) +// require.NoError(b, err) +// } +// _, err := tx.CreateBucket([]byte("my-bucket")) +// return err +// }) +// require.NoError(b, err) + +// b.ReportAllocs() +// b.ResetTimer() +// for i := 0; i < b.N; i++ { +// err = db.Update(func(tx *bolt.Tx) error { +// _, err := tx.CreateBucketIfNotExistsOld([]byte("my-bucket")) +// return err +// }) +// require.NoError(b, err) +// } +// } diff --git a/tx.go b/tx.go index a8e4d9d0b..7bf8acf5c 100644 --- a/tx.go +++ b/tx.go @@ -116,6 +116,10 @@ func (tx *Tx) CreateBucketIfNotExists(name []byte) (*Bucket, error) { return tx.root.CreateBucketIfNotExists(name) } +func (tx *Tx) CreateBucketIfNotExistsOld(name []byte) (*Bucket, error) { + return tx.root.CreateBucketIfNotExistsOld(name) +} + // DeleteBucket deletes a bucket. // Returns an error if the bucket cannot be found or if the key represents a non-bucket value. func (tx *Tx) DeleteBucket(name []byte) error {