From e728eb90da02e301aecbcabff96e691efcfe019c Mon Sep 17 00:00:00 2001 From: Ben Johnson Date: Wed, 14 May 2014 12:06:31 -0600 Subject: [PATCH] Minor stats fixes. --- bucket.go | 10 +++++++++- bucket_test.go | 44 +++++++++++++++++++++++++++++++++----------- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/bucket.go b/bucket.go index 96218f1..877c310 100644 --- a/bucket.go +++ b/bucket.go @@ -371,11 +371,14 @@ func (b *Bucket) Stats() BucketStats { b.forEachPage(func(p *page, depth int) { if (p.flags & leafPageFlag) != 0 { s.KeyN += int(p.count) + // used totals the used bytes for the page used := pageHeaderSize + if p.count != 0 { // If page has any elements, add all element headers. used += leafPageElementSize * int(p.count-1) + // Add all element key, value sizes. // The computation takes advantage of the fact that the position // of the last element's key/value equals to the total of the sizes @@ -384,6 +387,7 @@ func (b *Bucket) Stats() BucketStats { lastElement := p.leafPageElement(p.count - 1) used += int(lastElement.pos + lastElement.ksize + lastElement.vsize) } + if b.root == 0 { // For inlined bucket just update the inline stats s.InlineBucketInuse += used @@ -408,9 +412,11 @@ func (b *Bucket) Stats() BucketStats { } else if (p.flags & branchPageFlag) != 0 { s.BranchPageN++ lastElement := p.branchPageElement(p.count - 1) + // used totals the used bytes for the page // Add header and all element headers. used := pageHeaderSize + (branchPageElementSize * int(p.count-1)) + // Add size of all keys and values. // Again, use the fact that last element's position equals to // the total of key, value sizes of all previous elements. @@ -418,11 +424,13 @@ func (b *Bucket) Stats() BucketStats { s.BranchInuse += used s.BranchOverflowN += int(p.overflow) } + // Keep track of maximum page depth. if depth+1 > s.Depth { s.Depth = (depth + 1) } }) + // Alloc stats can be computed from page counts and pageSize. s.BranchAlloc = (s.BranchPageN + s.BranchOverflowN) * pageSize s.LeafAlloc = (s.LeafPageN + s.LeafOverflowN) * pageSize @@ -568,7 +576,7 @@ func (b *Bucket) maxInlineBucketSize() int { func (b *Bucket) write() []byte { // Allocate the appropriate size. var n = b.rootNode - var value = make([]byte, bucketHeaderSize+pageHeaderSize+n.size()) + var value = make([]byte, bucketHeaderSize+n.size()) // Write a bucket header. var bucket = (*bucket)(unsafe.Pointer(&value[0])) diff --git a/bucket_test.go b/bucket_test.go index 0be02e5..f6bd13f 100644 --- a/bucket_test.go +++ b/bucket_test.go @@ -566,7 +566,7 @@ func TestBucket_Stats(t *testing.T) { b, err := tx.CreateBucket([]byte("woojits")) assert.NoError(t, err) for i := 0; i < 500; i++ { - b.Put([]byte(strconv.Itoa(i)), []byte(strconv.Itoa(i))) + b.Put([]byte(fmt.Sprintf("%03d", i)), []byte(strconv.Itoa(i))) } b.Put(big_key, []byte(strings.Repeat("*", 10000))) @@ -582,15 +582,24 @@ func TestBucket_Stats(t *testing.T) { assert.Equal(t, 2, stats.LeafOverflowN, "LeafOverflowN") assert.Equal(t, 501, stats.KeyN, "KeyN") assert.Equal(t, 2, stats.Depth, "Depth") - assert.Equal(t, 125, stats.BranchInuse, "BranchInuse") - used := pageHeaderSize + 501*leafPageElementSize - used += 10*2 + 90*4 + 400*6 + len(big_key) + 10000 - assert.Equal(t, used, stats.LeafInuse, "LeafInuse") + + branchInuse := pageHeaderSize // branch page header + branchInuse += 6 * branchPageElementSize // branch elements + branchInuse += 6 * 3 // branch keys (6 3-byte keys) + assert.Equal(t, branchInuse, stats.BranchInuse, "BranchInuse") + + leafInuse := 6 * pageHeaderSize // leaf page header + leafInuse += 501 * leafPageElementSize // leaf elements + leafInuse += 500*3 + len(big_key) // leaf keys + leafInuse += 1*10 + 2*90 + 3*400 + 10000 // leaf values + assert.Equal(t, leafInuse, stats.LeafInuse, "LeafInuse") + if os.Getpagesize() == 4096 { // Incompatible page size assert.Equal(t, 4096, stats.BranchAlloc, "BranchAlloc") assert.Equal(t, 32768, stats.LeafAlloc, "LeafAlloc") } + assert.Equal(t, 1, stats.BucketN, "BucketN") assert.Equal(t, 0, stats.InlineBucketN, "InlineBucketN") assert.Equal(t, 0, stats.InlineBucketInuse, "InlineBucketInuse") @@ -672,13 +681,12 @@ func TestBucket_Stats_EmptyBucket(t *testing.T) { // Ensure a bucket can calculate stats. func TestBucket_Stats_Nested(t *testing.T) { - withOpenDB(func(db *DB, path string) { db.Update(func(tx *Tx) error { b, err := tx.CreateBucket([]byte("foo")) assert.NoError(t, err) for i := 0; i < 100; i++ { - b.Put([]byte(strconv.Itoa(i)), []byte(strconv.Itoa(i))) + b.Put([]byte(fmt.Sprintf("%02d", i)), []byte(fmt.Sprintf("%02d", i))) } bar, err := b.CreateBucket([]byte("bar")) assert.NoError(t, err) @@ -692,7 +700,9 @@ func TestBucket_Stats_Nested(t *testing.T) { } return nil }) + mustCheck(db) + db.View(func(tx *Tx) error { b := tx.Bucket([]byte("foo")) stats := b.Stats() @@ -703,10 +713,22 @@ func TestBucket_Stats_Nested(t *testing.T) { assert.Equal(t, 122, stats.KeyN, "KeyN") assert.Equal(t, 3, stats.Depth, "Depth") assert.Equal(t, 0, stats.BranchInuse, "BranchInuse") - baz := pageHeaderSize + 10*leafPageElementSize + 10*2 - foo := pageHeaderSize + 101*leafPageElementSize + 10*2 + 90*4 + 3 + bucketHeaderSize - bar := pageHeaderSize + 11*leafPageElementSize + 10*2 + 3 + bucketHeaderSize + baz - assert.Equal(t, foo+bar, stats.LeafInuse, "LeafInuse") + + foo := pageHeaderSize // foo + foo += 101 * leafPageElementSize // foo leaf elements + foo += 100*2 + 100*2 // foo leaf key/values + foo += 3 + bucketHeaderSize // foo -> bar key/value + + bar := pageHeaderSize // bar + bar += 11 * leafPageElementSize // bar leaf elements + bar += 10 + 10 // bar leaf key/values + bar += 3 + bucketHeaderSize // bar -> baz key/value + + baz := pageHeaderSize // baz (inline) + baz += 10 * leafPageElementSize // baz leaf elements + baz += 10 + 10 // baz leaf key/values + + assert.Equal(t, foo+bar+baz, stats.LeafInuse, "LeafInuse") if os.Getpagesize() == 4096 { // Incompatible page size assert.Equal(t, 0, stats.BranchAlloc, "BranchAlloc")