mirror of https://github.com/etcd-io/bbolt.git
Fix deletion reclamation.
parent
3d2e092a5d
commit
a5cb717fc7
|
@ -590,16 +590,13 @@ func (b *Bucket) free() {
|
||||||
|
|
||||||
// dereference removes all references to the old mmap.
|
// dereference removes all references to the old mmap.
|
||||||
func (b *Bucket) dereference() {
|
func (b *Bucket) dereference() {
|
||||||
for _, n := range b.nodes {
|
if b.rootNode != nil {
|
||||||
n.dereference()
|
b.rootNode.dereference()
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, child := range b.buckets {
|
for _, child := range b.buckets {
|
||||||
child.dereference()
|
child.dereference()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update statistics
|
|
||||||
b.tx.stats.NodeDeref += len(b.nodes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// pageNode returns the in-memory node, if it exists.
|
// pageNode returns the in-memory node, if it exists.
|
||||||
|
|
49
node.go
49
node.go
|
@ -2,8 +2,6 @@ package bolt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"sort"
|
"sort"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
@ -98,6 +96,9 @@ func (n *node) prevSibling() *node {
|
||||||
|
|
||||||
// put inserts a key/value.
|
// put inserts a key/value.
|
||||||
func (n *node) put(oldKey, newKey, value []byte, pgid pgid, flags uint32) {
|
func (n *node) put(oldKey, newKey, value []byte, pgid pgid, flags uint32) {
|
||||||
|
_assert(len(oldKey) > 0, "put: zero-length old key")
|
||||||
|
_assert(len(newKey) > 0, "put: zero-length new key")
|
||||||
|
|
||||||
// Find insertion index.
|
// Find insertion index.
|
||||||
index := sort.Search(len(n.inodes), func(i int) bool { return bytes.Compare(n.inodes[i].key, oldKey) != -1 })
|
index := sort.Search(len(n.inodes), func(i int) bool { return bytes.Compare(n.inodes[i].key, oldKey) != -1 })
|
||||||
|
|
||||||
|
@ -113,6 +114,7 @@ func (n *node) put(oldKey, newKey, value []byte, pgid pgid, flags uint32) {
|
||||||
inode.key = newKey
|
inode.key = newKey
|
||||||
inode.value = value
|
inode.value = value
|
||||||
inode.pgid = pgid
|
inode.pgid = pgid
|
||||||
|
_assert(len(inode.key) > 0, "put: zero-length inode key")
|
||||||
}
|
}
|
||||||
|
|
||||||
// del removes a key from the node.
|
// del removes a key from the node.
|
||||||
|
@ -150,11 +152,13 @@ func (n *node) read(p *page) {
|
||||||
inode.pgid = elem.pgid
|
inode.pgid = elem.pgid
|
||||||
inode.key = elem.key()
|
inode.key = elem.key()
|
||||||
}
|
}
|
||||||
|
_assert(len(inode.key) > 0, "read: zero-length inode key")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save first key so we can find the node in the parent when we spill.
|
// Save first key so we can find the node in the parent when we spill.
|
||||||
if len(n.inodes) > 0 {
|
if len(n.inodes) > 0 {
|
||||||
n.key = n.inodes[0].key
|
n.key = n.inodes[0].key
|
||||||
|
_assert(len(n.key) > 0, "read: zero-length node key")
|
||||||
} else {
|
} else {
|
||||||
n.key = nil
|
n.key = nil
|
||||||
}
|
}
|
||||||
|
@ -173,6 +177,8 @@ func (n *node) write(p *page) {
|
||||||
// Loop over each item and write it to the page.
|
// Loop over each item and write it to the page.
|
||||||
b := (*[maxAllocSize]byte)(unsafe.Pointer(&p.ptr))[n.pageElementSize()*len(n.inodes):]
|
b := (*[maxAllocSize]byte)(unsafe.Pointer(&p.ptr))[n.pageElementSize()*len(n.inodes):]
|
||||||
for i, item := range n.inodes {
|
for i, item := range n.inodes {
|
||||||
|
_assert(len(item.key) > 0, "write: zero-length inode key")
|
||||||
|
|
||||||
// Write the page element.
|
// Write the page element.
|
||||||
if n.isLeaf {
|
if n.isLeaf {
|
||||||
elem := p.leafPageElement(uint16(i))
|
elem := p.leafPageElement(uint16(i))
|
||||||
|
@ -194,7 +200,7 @@ func (n *node) write(p *page) {
|
||||||
b = b[len(item.value):]
|
b = b[len(item.value):]
|
||||||
}
|
}
|
||||||
|
|
||||||
// DEBUG ONLY: n.dump(os.Stderr)
|
// DEBUG ONLY: n.dump()
|
||||||
}
|
}
|
||||||
|
|
||||||
// split breaks up a node into smaller nodes, if appropriate.
|
// split breaks up a node into smaller nodes, if appropriate.
|
||||||
|
@ -290,6 +296,7 @@ func (n *node) spill() error {
|
||||||
|
|
||||||
node.parent.put(key, node.inodes[0].key, nil, node.pgid, 0)
|
node.parent.put(key, node.inodes[0].key, nil, node.pgid, 0)
|
||||||
node.key = node.inodes[0].key
|
node.key = node.inodes[0].key
|
||||||
|
_assert(len(n.key) > 0, "spill: zero-length node key")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the statistics.
|
// Update the statistics.
|
||||||
|
@ -393,6 +400,7 @@ func (n *node) rebalance() {
|
||||||
// Update target key on parent.
|
// Update target key on parent.
|
||||||
target.parent.put(target.key, target.inodes[0].key, nil, target.pgid, 0)
|
target.parent.put(target.key, target.inodes[0].key, nil, target.pgid, 0)
|
||||||
target.key = target.inodes[0].key
|
target.key = target.inodes[0].key
|
||||||
|
_assert(len(target.key) > 0, "rebalance(1): zero-length node key")
|
||||||
} else {
|
} else {
|
||||||
// Reparent and move node.
|
// Reparent and move node.
|
||||||
if child, ok := n.bucket.nodes[target.inodes[len(target.inodes)-1].pgid]; ok {
|
if child, ok := n.bucket.nodes[target.inodes[len(target.inodes)-1].pgid]; ok {
|
||||||
|
@ -409,6 +417,7 @@ func (n *node) rebalance() {
|
||||||
// Update parent key for node.
|
// Update parent key for node.
|
||||||
n.parent.put(n.key, n.inodes[0].key, nil, n.pgid, 0)
|
n.parent.put(n.key, n.inodes[0].key, nil, n.pgid, 0)
|
||||||
n.key = n.inodes[0].key
|
n.key = n.inodes[0].key
|
||||||
|
_assert(len(n.key) > 0, "rebalance(2): zero-length node key")
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -467,9 +476,12 @@ func (n *node) removeChild(target *node) {
|
||||||
// dereference causes the node to copy all its inode key/value references to heap memory.
|
// dereference causes the node to copy all its inode key/value references to heap memory.
|
||||||
// This is required when the mmap is reallocated so inodes are not pointing to stale data.
|
// This is required when the mmap is reallocated so inodes are not pointing to stale data.
|
||||||
func (n *node) dereference() {
|
func (n *node) dereference() {
|
||||||
key := make([]byte, len(n.key))
|
if n.key != nil {
|
||||||
copy(key, n.key)
|
key := make([]byte, len(n.key))
|
||||||
n.key = key
|
copy(key, n.key)
|
||||||
|
n.key = key
|
||||||
|
_assert(n.pgid == 0 || len(n.key) > 0, "dereference: zero-length node key on existing node")
|
||||||
|
}
|
||||||
|
|
||||||
for i := range n.inodes {
|
for i := range n.inodes {
|
||||||
inode := &n.inodes[i]
|
inode := &n.inodes[i]
|
||||||
|
@ -477,11 +489,20 @@ func (n *node) dereference() {
|
||||||
key := make([]byte, len(inode.key))
|
key := make([]byte, len(inode.key))
|
||||||
copy(key, inode.key)
|
copy(key, inode.key)
|
||||||
inode.key = key
|
inode.key = key
|
||||||
|
_assert(len(inode.key) > 0, "dereference: zero-length inode key")
|
||||||
|
|
||||||
value := make([]byte, len(inode.value))
|
value := make([]byte, len(inode.value))
|
||||||
copy(value, inode.value)
|
copy(value, inode.value)
|
||||||
inode.value = value
|
inode.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Recursively dereference children.
|
||||||
|
for _, child := range n.children {
|
||||||
|
child.dereference()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update statistics.
|
||||||
|
n.bucket.tx.stats.NodeDeref++
|
||||||
}
|
}
|
||||||
|
|
||||||
// free adds the node's underlying page to the freelist.
|
// free adds the node's underlying page to the freelist.
|
||||||
|
@ -492,30 +513,32 @@ func (n *node) free() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// dump writes the contents of the node for debugging purposes.
|
// dump writes the contents of the node to STDERR for debugging purposes.
|
||||||
func (n *node) dump(w io.Writer) {
|
/*
|
||||||
|
func (n *node) dump() {
|
||||||
// Write node header.
|
// Write node header.
|
||||||
var typ = "branch"
|
var typ = "branch"
|
||||||
if n.isLeaf {
|
if n.isLeaf {
|
||||||
typ = "leaf"
|
typ = "leaf"
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, "[NODE %d {type=%s count=%d}]\n", n.pgid, typ, len(n.inodes))
|
warnf("[NODE %d {type=%s count=%d}]", n.pgid, typ, len(n.inodes))
|
||||||
|
|
||||||
// Write out abbreviated version of each item.
|
// Write out abbreviated version of each item.
|
||||||
for _, item := range n.inodes {
|
for _, item := range n.inodes {
|
||||||
if n.isLeaf {
|
if n.isLeaf {
|
||||||
if item.flags&bucketLeafFlag != 0 {
|
if item.flags&bucketLeafFlag != 0 {
|
||||||
bucket := (*bucket)(unsafe.Pointer(&item.value[0]))
|
bucket := (*bucket)(unsafe.Pointer(&item.value[0]))
|
||||||
fmt.Fprintf(w, "[L \"%s\" -> (bucket root=%d)]\n", item.key, bucket.root)
|
warnf("+L %08x -> (bucket root=%d)", trunc(item.key, 4), bucket.root)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(w, "[L \"%s\" -> \"%s\"]\n", item.key, item.value)
|
warnf("+L %08x -> %08x", trunc(item.key, 4), trunc(item.value, 4))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(w, "[B \"%s\" -> pgid=%d]\n", item.key, item.pgid)
|
warnf("+B %08x -> pgid=%d", trunc(item.key, 4), item.pgid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Fprint(w, "\n")
|
warn("")
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// inode represents an internal node inside of a node.
|
// inode represents an internal node inside of a node.
|
||||||
// It can be used to point to elements in a page or point
|
// It can be used to point to elements in a page or point
|
||||||
|
|
|
@ -128,7 +128,7 @@ func simulateGetHandler(tx *Tx, qdb *QuickDB) {
|
||||||
// Retrieve root bucket.
|
// Retrieve root bucket.
|
||||||
b := tx.Bucket(keys[0])
|
b := tx.Bucket(keys[0])
|
||||||
if b == nil {
|
if b == nil {
|
||||||
panic(fmt.Sprintf("bucket[0] expected: %v\n", keys[0]))
|
panic(fmt.Sprintf("bucket[0] expected: %08x\n", trunc(keys[0], 4)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drill into nested buckets.
|
// Drill into nested buckets.
|
||||||
|
|
Loading…
Reference in New Issue