mirror of https://github.com/etcd-io/bbolt.git
first part
parent
4b56f820bc
commit
b9899d09ab
43
bucket.go
43
bucket.go
|
@ -129,16 +129,23 @@ func (b *Bucket) Bucket(name []byte) *Bucket {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise create a bucket and cache it.
|
// Otherwise create a bucket and cache it.
|
||||||
var child = newBucket(b.tx)
|
var child = b.openBucket(v)
|
||||||
child.bucket = &bucket{}
|
b.buckets[string(name)] = child
|
||||||
*child.bucket = *(*bucket)(unsafe.Pointer(&v[0]))
|
|
||||||
b.buckets[string(name)] = &child
|
|
||||||
|
|
||||||
// Save a reference to the inline page if the bucket is inline.
|
// Save a reference to the inline page if the bucket is inline.
|
||||||
if child.root == 0 {
|
if child.root == 0 {
|
||||||
child.page = (*page)(unsafe.Pointer(&v[bucketHeaderSize]))
|
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
|
return &child
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -354,7 +361,7 @@ func (b *Bucket) ForEach(fn func(k, v []byte) error) error {
|
||||||
|
|
||||||
// Stat returns stats on a bucket.
|
// Stat returns stats on a bucket.
|
||||||
func (b *Bucket) Stats() BucketStats {
|
func (b *Bucket) Stats() BucketStats {
|
||||||
var s BucketStats
|
var s, subStats BucketStats
|
||||||
pageSize := b.tx.db.pageSize
|
pageSize := b.tx.db.pageSize
|
||||||
b.forEachPage(func(p *page, depth int) {
|
b.forEachPage(func(p *page, depth int) {
|
||||||
if (p.flags & leafPageFlag) != 0 {
|
if (p.flags & leafPageFlag) != 0 {
|
||||||
|
@ -365,6 +372,13 @@ func (b *Bucket) Stats() BucketStats {
|
||||||
used += int(lastElement.pos + lastElement.ksize + lastElement.vsize)
|
used += int(lastElement.pos + lastElement.ksize + lastElement.vsize)
|
||||||
s.LeafInuse += used
|
s.LeafInuse += used
|
||||||
s.LeafOverflowN += int(p.overflow)
|
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 {
|
} else if (p.flags & branchPageFlag) != 0 {
|
||||||
s.BranchPageN++
|
s.BranchPageN++
|
||||||
lastElement := p.branchPageElement(p.count - 1)
|
lastElement := p.branchPageElement(p.count - 1)
|
||||||
|
@ -380,6 +394,10 @@ func (b *Bucket) Stats() BucketStats {
|
||||||
})
|
})
|
||||||
s.BranchAlloc = (s.BranchPageN + s.BranchOverflowN) * pageSize
|
s.BranchAlloc = (s.BranchPageN + s.BranchOverflowN) * pageSize
|
||||||
s.LeafAlloc = (s.LeafPageN + s.LeafOverflowN) * 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
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -643,6 +661,21 @@ type BucketStats struct {
|
||||||
LeafInuse int // bytes actually used for leaf data
|
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.
|
// cloneBytes returns a copy of a given slice.
|
||||||
func cloneBytes(v []byte) []byte {
|
func cloneBytes(v []byte) []byte {
|
||||||
var clone = make([]byte, len(v))
|
var clone = make([]byte, len(v))
|
||||||
|
|
|
@ -91,6 +91,14 @@ func NewApp() *cli.App {
|
||||||
Check(path)
|
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",
|
Name: "bench",
|
||||||
Usage: "Performs a synthetic benchmark",
|
Usage: "Performs a synthetic benchmark",
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
4
page.go
4
page.go
|
@ -63,7 +63,7 @@ func (p *page) leafPageElement(index uint16) *leafPageElement {
|
||||||
|
|
||||||
// leafPageElements retrieves a list of leaf nodes.
|
// leafPageElements retrieves a list of leaf nodes.
|
||||||
func (p *page) leafPageElements() []leafPageElement {
|
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
|
// 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.
|
// branchPageElements retrieves a list of branch nodes.
|
||||||
func (p *page) branchPageElements() []branchPageElement {
|
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.
|
// dump writes n bytes of the page to STDERR as hex output.
|
||||||
|
|
Loading…
Reference in New Issue