Skip to content

Commit

Permalink
Add page usage histogram.
Browse files Browse the repository at this point in the history
  • Loading branch information
benbjohnson committed Jun 6, 2014
1 parent e18a24b commit 2acdcbd
Show file tree
Hide file tree
Showing 5 changed files with 534 additions and 231 deletions.
6 changes: 4 additions & 2 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ func (h *handler) index(w http.ResponseWriter, r *http.Request) {

func (h *handler) page(w http.ResponseWriter, r *http.Request) {
err := h.db.View(func(tx *bolt.Tx) error {
showUsage := (r.FormValue("usage") == "true")

// Use the direct page id, if available.
if r.FormValue("id") != "" {
id, _ := strconv.Atoi(r.FormValue("id"))
return templates.Page(w, tx, nil, id)
return templates.Page(w, r, tx, nil, id, showUsage)
}

// Otherwise extract the indexes and traverse.
Expand All @@ -40,7 +42,7 @@ func (h *handler) page(w http.ResponseWriter, r *http.Request) {
return err
}

return templates.Page(w, tx, indexes, 0)
return templates.Page(w, r, tx, indexes, 0, showUsage)
})
if err != nil {
templates.Error(w, err)
Expand Down
94 changes: 78 additions & 16 deletions templates/bolt.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ package templates
import (
"fmt"
"unsafe"

"github.com/boltdb/bolt"
)

const pageHeaderSize = int(unsafe.Offsetof(((*page)(nil)).ptr))
Expand Down Expand Up @@ -37,10 +39,11 @@ type page struct {
ptr uintptr
}

type pageStats struct {
type stats struct {
inuse int
alloc int
utilization float64
histogram map[int]int
}

// typ returns a human readable page type string used for debugging.
Expand Down Expand Up @@ -70,34 +73,49 @@ func (p *page) branchPageElement(index uint16) *branchPageElement {
return &((*[maxNodesPerPage]branchPageElement)(unsafe.Pointer(&p.ptr)))[index]
}

func (p *page) stats(pageSize int) pageStats {
var s pageStats
// stats calcuates statistics for a page.
func (p *page) stats(pageSize int) stats {
var s stats
s.alloc = (int(p.overflow) + 1) * pageSize
s.inuse = p.inuse()

// Calculate space utilitization
if s.alloc > 0 {
s.utilization = float64(s.inuse) / float64(s.alloc)
}

return s
}

// inuse returns the number of bytes used in a given page.
func (p *page) inuse() int {
var n int
if (p.flags & leafPageFlag) != 0 {
s.inuse = pageHeaderSize
n = pageHeaderSize
if p.count > 0 {
s.inuse += leafPageElementSize * int(p.count-1)
n += leafPageElementSize * int(p.count-1)
e := p.leafPageElement(p.count - 1)
s.inuse += int(e.pos + e.ksize + e.vsize)
n += int(e.pos + e.ksize + e.vsize)
}

} else if (p.flags & branchPageFlag) != 0 {
s.inuse = pageHeaderSize
n = pageHeaderSize
if p.count > 0 {
s.inuse += branchPageElementSize * int(p.count-1)
n += branchPageElementSize * int(p.count-1)
e := p.branchPageElement(p.count - 1)
s.inuse += int(e.pos + e.ksize)
n += int(e.pos + e.ksize)
}

}

// Calculate space utilitization
if s.alloc > 0 {
s.utilization = float64(s.inuse) / float64(s.alloc)
}
return n
}

return s
// usage calculates a histogram of page sizes within nested pages.
func usage(tx *bolt.Tx, pgid pgid) map[int]int {
m := make(map[int]int)
forEachPage(tx, pgid, func(p *page) {
m[p.inuse()]++
})
return m
}

// branchPageElement represents a node on a branch page.
Expand Down Expand Up @@ -163,3 +181,47 @@ type tx struct {
root bucket
// remaining fields not used.
}

// find locates a page using either a set of page indices or a direct page id.
// It returns a list of parent page numbers if the indices are used.
// It also returns the page reference.
func find(tx *bolt.Tx, directID int, indexes []int) (*page, []pgid, error) {
// If a direct ID is provided then just use it.
if directID != 0 {
return pageAt(tx, pgid(directID)), nil, nil
}

// Otherwise traverse the pages index.
ids, err := pgids(tx, indexes)
if err != nil {
return nil, nil, err
}

return pageAt(tx, ids[len(ids)-1]), ids, nil
}

// retrieves the page from a given transaction.
func pageAt(tx *bolt.Tx, id pgid) *page {
info := tx.DB().Info()
return (*page)(unsafe.Pointer(&info.Data[info.PageSize*int(id)]))
}

// forEachPage recursively iterates over all pages starting at a given page.
func forEachPage(tx *bolt.Tx, pgid pgid, fn func(*page)) {
p := pageAt(tx, pgid)
fn(p)

if (p.flags & leafPageFlag) != 0 {
for i := 0; i < int(p.count); i++ {
if e := p.leafPageElement(uint16(i)); (e.flags & bucketLeafFlag) != 0 {
if b := (*bucket)(unsafe.Pointer(&e.value()[0])); b.root != 0 {
forEachPage(tx, b.root, fn)
}
}
}
} else if (p.flags & branchPageFlag) != 0 {
for i := 0; i < int(p.count); i++ {
forEachPage(tx, p.branchPageElement(uint16(i)).pgid, fn)
}
}
}
Loading

0 comments on commit 2acdcbd

Please sign in to comment.