diff options
Diffstat (limited to 'cmd/bolt/bench')
-rw-r--r-- | cmd/bolt/bench/bench.go | 126 | ||||
-rw-r--r-- | cmd/bolt/bench/config.go | 7 | ||||
-rw-r--r-- | cmd/bolt/bench/generate.go | 24 |
3 files changed, 157 insertions, 0 deletions
diff --git a/cmd/bolt/bench/bench.go b/cmd/bolt/bench/bench.go new file mode 100644 index 0000000..df584f2 --- /dev/null +++ b/cmd/bolt/bench/bench.go @@ -0,0 +1,126 @@ +package bench + +import ( + "errors" + "fmt" + "sync" + "testing" + + "github.com/boltdb/bolt" +) + +const ( + BenchReadMode = "read" + BenchWriteMode = "write" + BenchSequentialTraversal = "sequential" + BenchRandomTraversal = "random" +) + +type Benchmark struct { + db *bolt.DB + config *Config +} + +func New(db *bolt.DB, config *Config) *Benchmark { + b := new(Benchmark) + b.db = db + b.config = config + return b +} + +func (bm *Benchmark) Run(b *testing.B) { + + // Read buckets and keys before benchmark begins so we don't knew the + // results. + buckets, err := buckets(bm.db) + if err != nil { + b.Fatalf("error: %+v", err) + } + bucketsWithKeys := make(map[string][]string) + for _, bucket := range buckets { + keys, err := keys(bm.db, bucket) + if err != nil { + b.Fatalf("error: %+v", err) + } + bucketsWithKeys[bucket] = keys + } + + b.ResetTimer() + + // Keep running a fixed number of parallel reads until we run out of time. + for i := 0; i < b.N; i++ { + var wg sync.WaitGroup + for j := 0; j < bm.config.Parallelism; j++ { + wg.Add(1) + go func() { + defer wg.Done() + if err := bm.readBuckets(b, bm.db, bucketsWithKeys); err != nil { + b.Fatalf("error: %+v", err) + } + }() + } + wg.Wait() + } +} + +// Run benchmark(s) for each of the given buckets. +func (bm *Benchmark) readBuckets(b *testing.B, db *bolt.DB, bucketsWithKeys map[string][]string) error { + return db.View(func(tx *bolt.Tx) error { + bucketsCount := len(bucketsWithKeys) + count := 0 + for bucket, keys := range bucketsWithKeys { + bucket := tx.Bucket([]byte(bucket)) + if err := bm.readKeys(b, bucket, keys); err != nil { + return err + } + count++ + } + if count != bucketsCount { + return errors.New(fmt.Sprintf("wrong count: %d; expected: %d", count, bucketsCount)) + } + return nil + }) +} + +func (bm *Benchmark) readKeys(b *testing.B, bucket *bolt.Bucket, keys []string) error { + c := bucket.Cursor() + keysCount := len(keys) + count := 0 + for k, _ := c.First(); k != nil; k, _ = c.Next() { + count++ + } + if count != keysCount { + return errors.New(fmt.Sprintf("wrong count: %d; expected: %d", count, keysCount)) + } + return nil +} + +func buckets(db *bolt.DB) ([]string, error) { + buckets := []string{} + err := db.View(func(tx *bolt.Tx) error { + // Iterate over each bucket. + return tx.ForEach(func(name []byte, _ *bolt.Bucket) error { + buckets = append(buckets, string(name)) + return nil + }) + }) + return buckets, err +} + +func keys(db *bolt.DB, bucket string) ([]string, error) { + keys := []string{} + err := db.View(func(tx *bolt.Tx) error { + // Find bucket. + b := tx.Bucket([]byte(bucket)) + if b == nil { + return errors.New(fmt.Sprintf("bucket %+v not found", b)) + } + + // Iterate over each key. + return b.ForEach(func(key, _ []byte) error { + keys = append(keys, string(key)) + return nil + }) + }) + return keys, err +} diff --git a/cmd/bolt/bench/config.go b/cmd/bolt/bench/config.go new file mode 100644 index 0000000..dea08fd --- /dev/null +++ b/cmd/bolt/bench/config.go @@ -0,0 +1,7 @@ +package bench + +type Config struct { + ReadWriteMode string + TraversalPattern string + Parallelism int +} diff --git a/cmd/bolt/bench/generate.go b/cmd/bolt/bench/generate.go new file mode 100644 index 0000000..8c5554d --- /dev/null +++ b/cmd/bolt/bench/generate.go @@ -0,0 +1,24 @@ +package bench + +import ( + "fmt" + "strings" + + "github.com/boltdb/bolt" +) + +// Generate and write data to specified number of buckets/items. +func GenerateDB(db *bolt.DB, numBuckets, numItemsPerBucket int) error { + return db.Update(func(tx *bolt.Tx) error { + for bucketIndex := 0; bucketIndex < numBuckets; bucketIndex++ { + bucketName := fmt.Sprintf("bucket%08d") + tx.CreateBucket([]byte(bucketName)) + bucket := tx.Bucket([]byte(bucketName)) + for i := 0; i < numItemsPerBucket; i++ { + value := []byte(strings.Repeat("0", 100)) + bucket.Put([]byte(fmt.Sprintf("key%08d", i)), value) + } + } + return nil + }) +} |