diff options
| author | Ben Johnson <benbjohnson@yahoo.com> | 2014-05-14 12:20:46 -0600 |
|---|---|---|
| committer | Ben Johnson <benbjohnson@yahoo.com> | 2014-05-14 12:20:46 -0600 |
| commit | a6d6d964b6a87873139aa8b621185a95b66b6542 (patch) | |
| tree | 01c4e2d71046303967eb60f1d409bdf73833a155 /cmd | |
| parent | Merge pull request #161 from benbjohnson/work (diff) | |
| parent | Minor stats fixes. (diff) | |
| download | dedo-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.go | 8 | ||||
| -rw-r--r-- | cmd/bolt/stats.go | 77 | ||||
| -rw-r--r-- | cmd/bolt/stats_test.go | 62 |
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) + }) +} |
