Optimize Tx.Check().

This commit removes several memory allocations occurring on every page and also caches the freelist map used when iterating over the pages. This results in significantly better performance.
master
Ben Johnson 2014-05-28 12:50:46 -06:00
parent b789691976
commit 754966bea0
3 changed files with 15 additions and 14 deletions

View File

@ -38,7 +38,7 @@ func Check(path string) {
// Print summary of errors.
if count > 0 {
fatalf("%d errors found")
fatalf("%d errors found", count)
} else {
println("OK")
}

View File

@ -10,11 +10,13 @@ import (
"github.com/boltdb/bolt"
"github.com/codegangsta/cli"
// "github.com/davecheney/profile"
)
var branch, commit string
func main() {
// defer profile.Start(&profile.Config{CPUProfile: true, MemProfile: true}).Stop()
log.SetFlags(0)
NewApp().Run(os.Args)
}

25
tx.go
View File

@ -316,15 +316,13 @@ func (tx *Tx) check(ch chan error) {
}
// Recursively check buckets.
tx.checkBucket(&tx.root, reachable, ch)
tx.checkBucket(&tx.root, reachable, freed, ch)
// Ensure all pages below high water mark are either reachable or freed.
for i := pgid(0); i < tx.meta.pgid; i++ {
_, isReachable := reachable[i]
if !isReachable && !freed[i] {
ch <- fmt.Errorf("page %d: unreachable unfreed", int(i))
} else if isReachable && freed[i] {
ch <- fmt.Errorf("page %d: reachable freed", int(i))
}
}
@ -332,7 +330,7 @@ func (tx *Tx) check(ch chan error) {
close(ch)
}
func (tx *Tx) checkBucket(b *Bucket, reachable map[pgid]*page, ch chan error) {
func (tx *Tx) checkBucket(b *Bucket, reachable map[pgid]*page, freed map[pgid]bool, ch chan error) {
// Ignore inline buckets.
if b.root == 0 {
return
@ -340,6 +338,10 @@ func (tx *Tx) checkBucket(b *Bucket, reachable map[pgid]*page, ch chan error) {
// Check every page used by this bucket.
b.tx.forEachPage(b.root, 0, func(p *page, _ int) {
if p.id > tx.meta.pgid {
ch <- fmt.Errorf("page %d: out of bounds: %d", int(p.id), int(b.tx.meta.pgid))
}
// Ensure each page is only referenced once.
for i := pgid(0); i <= pgid(p.overflow); i++ {
var id = p.id + i
@ -349,21 +351,18 @@ func (tx *Tx) checkBucket(b *Bucket, reachable map[pgid]*page, ch chan error) {
reachable[id] = p
}
// Retrieve page info.
info, err := b.tx.Page(int(p.id))
if err != nil {
ch <- err
} else if info == nil {
ch <- fmt.Errorf("page %d: out of bounds: %d", int(p.id), int(b.tx.meta.pgid))
} else if info.Type != "branch" && info.Type != "leaf" {
ch <- fmt.Errorf("page %d: invalid type: %s", int(p.id), info.Type)
// We should only encounter un-freed leaf and branch pages.
if freed[p.id] {
ch <- fmt.Errorf("page %d: reachable freed", int(p.id))
} else if (p.flags&branchPageFlag) == 0 && (p.flags&leafPageFlag) == 0 {
ch <- fmt.Errorf("page %d: invalid type: %s", int(p.id), p.typ())
}
})
// Check each bucket within this bucket.
_ = b.ForEach(func(k, v []byte) error {
if child := b.Bucket(k); child != nil {
tx.checkBucket(child, reachable, ch)
tx.checkBucket(child, reachable, freed, ch)
}
return nil
})