aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Kobetic <mkobetic@gmail.com>2014-05-09 20:50:55 +0000
committerMartin Kobetic <mkobetic@gmail.com>2014-05-09 20:50:55 +0000
commitb9899d09ab6c032b54fe4a09e9389dff28d8a7f8 (patch)
tree30a0bdbcd865fd26bbb268afab23f3f68d3f692a
parentMerge pull request #160 from benbjohnson/fix-deletion (diff)
downloaddedo-b9899d09ab6c032b54fe4a09e9389dff28d8a7f8.tar.gz
dedo-b9899d09ab6c032b54fe4a09e9389dff28d8a7f8.tar.xz
first part
-rw-r--r--bucket.go43
-rw-r--r--cmd/bolt/main.go8
-rw-r--r--cmd/bolt/stats.go54
-rw-r--r--page.go4
4 files changed, 102 insertions, 7 deletions
diff --git a/bucket.go b/bucket.go
index 43204f7..41156d7 100644
--- a/bucket.go
+++ b/bucket.go
@@ -129,16 +129,23 @@ func (b *Bucket) Bucket(name []byte) *Bucket {
}
// Otherwise create a bucket and cache it.
- var child = newBucket(b.tx)
- child.bucket = &bucket{}
- *child.bucket = *(*bucket)(unsafe.Pointer(&v[0]))
- b.buckets[string(name)] = &child
+ var child = b.openBucket(v)
+ b.buckets[string(name)] = child
// Save a reference to the inline page if the bucket is inline.
if child.root == 0 {
child.page = (*page)(unsafe.Pointer(&v[bucketHeaderSize]))
}
+ return child
+}
+
+// Helper method that re-interprets a sub-bucket value
+// from a parent into a Bucket
+func (b *Bucket) openBucket(value []byte) *Bucket {
+ var child = newBucket(b.tx)
+ child.bucket = &bucket{}
+ *child.bucket = *(*bucket)(unsafe.Pointer(&value[0]))
return &child
}
@@ -354,7 +361,7 @@ func (b *Bucket) ForEach(fn func(k, v []byte) error) error {
// Stat returns stats on a bucket.
func (b *Bucket) Stats() BucketStats {
- var s BucketStats
+ var s, subStats BucketStats
pageSize := b.tx.db.pageSize
b.forEachPage(func(p *page, depth int) {
if (p.flags & leafPageFlag) != 0 {
@@ -365,6 +372,13 @@ func (b *Bucket) Stats() BucketStats {
used += int(lastElement.pos + lastElement.ksize + lastElement.vsize)
s.LeafInuse += used
s.LeafOverflowN += int(p.overflow)
+
+ // Recurse into sub-buckets
+ for _, e := range p.leafPageElements() {
+ if e.flags&bucketLeafFlag != 0 {
+ subStats.Add(b.openBucket(e.value()).Stats())
+ }
+ }
} else if (p.flags & branchPageFlag) != 0 {
s.BranchPageN++
lastElement := p.branchPageElement(p.count - 1)
@@ -380,6 +394,10 @@ func (b *Bucket) Stats() BucketStats {
})
s.BranchAlloc = (s.BranchPageN + s.BranchOverflowN) * pageSize
s.LeafAlloc = (s.LeafPageN + s.LeafOverflowN) * pageSize
+
+ // add the max depth of sub-buckets to get total nested depth
+ s.Depth += subStats.Depth
+ s.Add(subStats)
return s
}
@@ -643,6 +661,21 @@ type BucketStats struct {
LeafInuse int // bytes actually used for leaf data
}
+func (s *BucketStats) Add(other BucketStats) {
+ s.BranchPageN += other.BranchPageN
+ s.BranchOverflowN += other.BranchOverflowN
+ s.LeafPageN += other.LeafPageN
+ s.LeafOverflowN += other.LeafOverflowN
+ s.KeyN += s.KeyN
+ if s.Depth < other.Depth {
+ s.Depth = other.Depth
+ }
+ s.BranchAlloc += other.BranchAlloc
+ s.BranchInuse += other.BranchInuse
+ s.LeafAlloc += other.LeafAlloc
+ s.LeafInuse += other.LeafInuse
+}
+
// cloneBytes returns a copy of a given slice.
func cloneBytes(v []byte) []byte {
var clone = make([]byte, len(v))
diff --git a/cmd/bolt/main.go b/cmd/bolt/main.go
index 3397042..659f1c3 100644
--- a/cmd/bolt/main.go
+++ b/cmd/bolt/main.go
@@ -92,6 +92,14 @@ func NewApp() *cli.App {
},
},
{
+ Name: "stats",
+ Usage: "Retrieve statistics for a bucket (aggregated recursively)",
+ 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..da344d0
--- /dev/null
+++ b/cmd/bolt/stats.go
@@ -0,0 +1,54 @@
+package main
+
+import (
+ "os"
+
+ "github.com/boltdb/bolt"
+)
+
+// Keys retrieves a list of keys for a given bucket.
+func Stats(path, name 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 {
+ // Find bucket.
+ b := tx.Bucket([]byte(name))
+ if b == nil {
+ fatalf("bucket not found: %s", name)
+ return nil
+ }
+
+ // Iterate over each key.
+ s := b.Stats()
+ 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)
+ printf("\tBytes actually used for branch data: %d\n", s.BranchInuse)
+ printf("\tBytes allocated for physical leaf pages: %d\n", s.LeafAlloc)
+ printf("\tBytes actually used for leaf data: %d\n", s.LeafInuse)
+ return nil
+ })
+ if err != nil {
+ fatal(err)
+ return
+ }
+}
diff --git a/page.go b/page.go
index 56cf064..40e2421 100644
--- a/page.go
+++ b/page.go
@@ -63,7 +63,7 @@ func (p *page) leafPageElement(index uint16) *leafPageElement {
// leafPageElements retrieves a list of leaf nodes.
func (p *page) leafPageElements() []leafPageElement {
- return ((*[maxNodesPerPage]leafPageElement)(unsafe.Pointer(&p.ptr)))[:]
+ return ((*[maxNodesPerPage]leafPageElement)(unsafe.Pointer(&p.ptr)))[:p.count]
}
// branchPageElement retrieves the branch node by index
@@ -73,7 +73,7 @@ func (p *page) branchPageElement(index uint16) *branchPageElement {
// branchPageElements retrieves a list of branch nodes.
func (p *page) branchPageElements() []branchPageElement {
- return ((*[maxNodesPerPage]branchPageElement)(unsafe.Pointer(&p.ptr)))[:]
+ return ((*[maxNodesPerPage]branchPageElement)(unsafe.Pointer(&p.ptr)))[:p.count]
}
// dump writes n bytes of the page to STDERR as hex output.