Bucket stats.

pull/34/head
Ben Johnson 2014-02-21 09:20:45 -07:00
parent ce3fa8a396
commit 1028d571d8
3 changed files with 107 additions and 0 deletions

View File

@ -29,3 +29,32 @@ func (b *Bucket) cursor() *Cursor {
stack: make([]pageElementRef, 0),
}
}
// Stat returns stats on a bucket.
func (b *Bucket) Stat() *BucketStat {
s := &BucketStat{}
b.transaction.forEachPage(b.root, 0, func(p *page, depth int) {
if (p.flags & leafPageFlag) != 0 {
s.LeafPageCount++
s.KeyCount += int(p.count)
} else if (p.flags & branchPageFlag) != 0 {
s.BranchPageCount++
}
s.OverflowPageCount += int(p.overflow)
if depth+1 > s.MaxDepth {
s.MaxDepth = (depth + 1)
}
})
return s
}
// BucketStat represents stats on a bucket such as branch pages and leaf pages.
type BucketStat struct {
BranchPageCount int
LeafPageCount int
OverflowPageCount int
KeyCount int
MaxDepth int
}

62
bucket_test.go Normal file
View File

@ -0,0 +1,62 @@
package bolt
import (
"strconv"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
// Ensure a bucket can calculate stats.
func TestBucketStat(t *testing.T) {
withOpenDB(func(db *DB, path string) {
db.Do(func(txn *RWTransaction) error {
// Add bucket with lots of keys.
txn.CreateBucket("widgets")
for i := 0; i < 100000; i++ {
txn.Put("widgets", []byte(strconv.Itoa(i)), []byte(strconv.Itoa(i)))
}
// Add bucket with fewer keys but one big value.
txn.CreateBucket("woojits")
for i := 0; i < 500; i++ {
txn.Put("woojits", []byte(strconv.Itoa(i)), []byte(strconv.Itoa(i)))
}
txn.Put("woojits", []byte("really-big-value"), []byte(strings.Repeat("*", 10000)))
// Add a bucket that fits on a single root leaf.
txn.CreateBucket("whozawhats")
txn.Put("whozawhats", []byte("foo"), []byte("bar"))
return nil
})
db.With(func(txn *Transaction) error {
b := txn.Bucket("widgets")
stat := b.Stat()
assert.Equal(t, stat.BranchPageCount, 15)
assert.Equal(t, stat.LeafPageCount, 1281)
assert.Equal(t, stat.OverflowPageCount, 0)
assert.Equal(t, stat.KeyCount, 100000)
assert.Equal(t, stat.MaxDepth, 3)
b = txn.Bucket("woojits")
stat = b.Stat()
assert.Equal(t, stat.BranchPageCount, 1)
assert.Equal(t, stat.LeafPageCount, 6)
assert.Equal(t, stat.OverflowPageCount, 2)
assert.Equal(t, stat.KeyCount, 501)
assert.Equal(t, stat.MaxDepth, 2)
b = txn.Bucket("whozawhats")
stat = b.Stat()
assert.Equal(t, stat.BranchPageCount, 0)
assert.Equal(t, stat.LeafPageCount, 1)
assert.Equal(t, stat.OverflowPageCount, 0)
assert.Equal(t, stat.KeyCount, 1)
assert.Equal(t, stat.MaxDepth, 1)
return nil
})
})
}

View File

@ -134,3 +134,19 @@ func (t *Transaction) page(id pgid) *page {
// Otherwise return directly from the mmap.
return t.db.page(id)
}
// forEachPage iterates over every page within a given page and executes a function.
func (t *Transaction) forEachPage(pgid pgid, depth int, fn func(*page, int)) {
p := t.page(pgid)
// Execute function.
fn(p, depth)
// Recursively loop over children.
if (p.flags & branchPageFlag) != 0 {
for i := 0; i < int(p.count); i++ {
elem := p.branchPageElement(uint16(i))
t.forEachPage(elem.pgid, depth+1, fn)
}
}
}