aboutsummaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
authorBen Johnson <benbjohnson@yahoo.com>2014-05-14 12:20:46 -0600
committerBen Johnson <benbjohnson@yahoo.com>2014-05-14 12:20:46 -0600
commita6d6d964b6a87873139aa8b621185a95b66b6542 (patch)
tree01c4e2d71046303967eb60f1d409bdf73833a155 /cmd
parentMerge pull request #161 from benbjohnson/work (diff)
parentMinor stats fixes. (diff)
downloaddedo-a6d6d964b6a87873139aa8b621185a95b66b6542.tar.gz
dedo-a6d6d964b6a87873139aa8b621185a95b66b6542.tar.xz
Merge pull request #162 from Shopify/nested_stats2
Recursive/aggregate bucket stats
Diffstat (limited to 'cmd')
-rw-r--r--cmd/bolt/main.go8
-rw-r--r--cmd/bolt/stats.go77
-rw-r--r--cmd/bolt/stats_test.go62
3 files changed, 147 insertions, 0 deletions
diff --git a/cmd/bolt/main.go b/cmd/bolt/main.go
index 82b5e5c..66c33d2 100644
--- a/cmd/bolt/main.go
+++ b/cmd/bolt/main.go
@@ -92,6 +92,14 @@ func NewApp() *cli.App {
},
},
{
+ Name: "stats",
+ Usage: "Aggregate statistics for all buckets matching specified prefix",
+ Action: func(c *cli.Context) {
+ path, name := c.Args().Get(0), c.Args().Get(1)
+ Stats(path, name)
+ },
+ },
+ {
Name: "bench",
Usage: "Performs a synthetic benchmark",
Flags: []cli.Flag{
diff --git a/cmd/bolt/stats.go b/cmd/bolt/stats.go
new file mode 100644
index 0000000..54a0f44
--- /dev/null
+++ b/cmd/bolt/stats.go
@@ -0,0 +1,77 @@
+package main
+
+import (
+ "bytes"
+ "os"
+
+ "github.com/boltdb/bolt"
+)
+
+// 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)
+ 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
+ }
+}
diff --git a/cmd/bolt/stats_test.go b/cmd/bolt/stats_test.go
new file mode 100644
index 0000000..2ad5d51
--- /dev/null
+++ b/cmd/bolt/stats_test.go
@@ -0,0 +1,62 @@
+package main_test
+
+import (
+ "os"
+ "strconv"
+ "testing"
+
+ "github.com/boltdb/bolt"
+ . "github.com/boltdb/bolt/cmd/bolt"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestStats(t *testing.T) {
+ if os.Getpagesize() != 4096 {
+ t.Skip()
+ }
+ SetTestMode(true)
+ open(func(db *bolt.DB, path string) {
+ db.Update(func(tx *bolt.Tx) error {
+ b, err := tx.CreateBucket([]byte("foo"))
+ if err != nil {
+ return err
+ }
+ for i := 0; i < 10; i++ {
+ b.Put([]byte(strconv.Itoa(i)), []byte(strconv.Itoa(i)))
+ }
+ b, err = tx.CreateBucket([]byte("bar"))
+ if err != nil {
+ return err
+ }
+ for i := 0; i < 100; i++ {
+ b.Put([]byte(strconv.Itoa(i)), []byte(strconv.Itoa(i)))
+ }
+ b, err = tx.CreateBucket([]byte("baz"))
+ if err != nil {
+ return err
+ }
+ b.Put([]byte("key"), []byte("value"))
+ return nil
+ })
+ db.Close()
+ output := run("stats", path, "b")
+ assert.Equal(t, "Aggregate statistics for 2 buckets\n\n"+
+ "Page count statistics\n"+
+ "\tNumber of logical branch pages: 0\n"+
+ "\tNumber of physical branch overflow pages: 0\n"+
+ "\tNumber of logical leaf pages: 1\n"+
+ "\tNumber of physical leaf overflow pages: 0\n"+
+ "Tree statistics\n"+
+ "\tNumber of keys/value pairs: 101\n"+
+ "\tNumber of levels in B+tree: 1\n"+
+ "Page size utilization\n"+
+ "\tBytes allocated for physical branch pages: 0\n"+
+ "\tBytes actually used for branch data: 0 (0%)\n"+
+ "\tBytes allocated for physical leaf pages: 4096\n"+
+ "\tBytes actually used for leaf data: 1996 (48%)\n"+
+ "Bucket statistics\n"+
+ "\tTotal number of buckets: 2\n"+
+ "\tTotal number on inlined buckets: 1 (50%)\n"+
+ "\tBytes used for inlined buckets: 40 (2%)", output)
+ })
+}