aboutsummaryrefslogtreecommitdiff
path: root/bucket.go
diff options
context:
space:
mode:
Diffstat (limited to 'bucket.go')
-rw-r--r--bucket.go94
1 files changed, 17 insertions, 77 deletions
diff --git a/bucket.go b/bucket.go
index 9737128..0b9c17d 100644
--- a/bucket.go
+++ b/bucket.go
@@ -4,7 +4,6 @@ import (
"bytes"
"errors"
"fmt"
- "sort"
"unsafe"
)
@@ -41,10 +40,10 @@ var (
// Bucket represents a collection of key/value pairs inside the database.
type Bucket struct {
*bucket
- tx *Tx
- buckets map[string]*Bucket
- nodes map[pgid]*node
- pending []*node
+ tx *Tx
+ buckets map[string]*Bucket
+ rootNode *node
+ nodes map[pgid]*node
}
// bucket represents the on-file representation of a bucket.
@@ -382,76 +381,19 @@ func (b *Bucket) spill() error {
c.node().put([]byte(name), []byte(name), value, 0, bucketLeafFlag)
}
- // Ignore if there are no nodes to spill.
- if len(b.nodes) == 0 {
+ // Ignore if there's not a materialized root node.
+ if b.rootNode == nil {
return nil
}
- // Sort nodes by highest depth first.
- nodes := make(nodesByDepth, 0, len(b.nodes))
- for _, n := range b.nodes {
- nodes = append(nodes, n)
- }
- sort.Sort(nodes)
-
- // Spill nodes by deepest first.
- for i := 0; i < len(nodes); i++ {
- n := nodes[i]
-
- // Split nodes into appropriate sized nodes.
- // The first node in this list will be a reference to n to preserve ancestry.
- newNodes := n.split(b.tx.db.pageSize)
- b.pending = newNodes
-
- // If this is a root node that split then create a parent node.
- if n.parent == nil && len(newNodes) > 1 {
- n.parent = &node{bucket: b, isLeaf: false}
- nodes = append(nodes, n.parent)
- }
-
- // Add node's page to the freelist.
- if n.pgid > 0 {
- b.tx.db.freelist.free(b.tx.id(), b.tx.page(n.pgid))
- }
-
- // Write nodes to dirty pages.
- for i, newNode := range newNodes {
- // Allocate contiguous space for the node.
- p, err := b.tx.allocate((newNode.size() / b.tx.db.pageSize) + 1)
- if err != nil {
- return err
- }
-
- // Write the node to the page.
- newNode.write(p)
- newNode.pgid = p.id
- newNode.parent = n.parent
-
- // The first node should use the existing entry, other nodes are inserts.
- var oldKey []byte
- if i == 0 {
- oldKey = n.key
- } else {
- oldKey = newNode.inodes[0].key
- }
-
- // Update the parent entry.
- if newNode.parent != nil {
- newNode.parent.put(oldKey, newNode.inodes[0].key, nil, newNode.pgid, 0)
- }
-
- // Update the statistics.
- b.tx.stats.Spill++
- }
-
- b.pending = nil
+ // Spill nodes.
+ if err := b.rootNode.spill(); err != nil {
+ return err
}
-
- // Clear out nodes now that they are all spilled.
- b.nodes = make(map[pgid]*node)
+ b.rootNode = b.rootNode.root()
// Update the root node for this bucket.
- b.root = nodes[len(nodes)-1].pgid
+ b.root = b.rootNode.pgid
return nil
}
@@ -474,10 +416,12 @@ func (b *Bucket) node(pgid pgid, parent *node) *node {
return n
}
- // Otherwise create a branch and cache it.
+ // Otherwise create a branch node and cache it.
n := &node{bucket: b, parent: parent}
- if n.parent != nil {
- n.depth = n.parent.depth + 1
+ if parent == nil {
+ b.rootNode = n
+ } else {
+ parent.children = append(parent.children, n)
}
n.read(b.tx.page(pgid))
b.nodes[pgid] = n
@@ -494,16 +438,12 @@ func (b *Bucket) dereference() {
n.dereference()
}
- for _, n := range b.pending {
- n.dereference()
- }
-
for _, child := range b.buckets {
child.dereference()
}
// Update statistics
- b.tx.stats.NodeDeref += len(b.nodes) + len(b.pending)
+ b.tx.stats.NodeDeref += len(b.nodes)
}
// pageNode returns the in-memory node, if it exists.