first part

master
Martin Kobetic 2014-05-09 20:50:55 +00:00
parent 4b56f820bc
commit b9899d09ab
4 changed files with 102 additions and 7 deletions

View File

@ -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))

View File

@ -91,6 +91,14 @@ func NewApp() *cli.App {
Check(path)
},
},
{
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",

54
cmd/bolt/stats.go Normal file
View File

@ -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
}
}

View File

@ -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.