aboutsummaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/bolt/bench/bench.go126
-rw-r--r--cmd/bolt/bench/config.go7
-rw-r--r--cmd/bolt/bench/generate.go24
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
+ })
+}