fix rebalance bug

This commit fixes a rare issue where a page can become accessible
when it has already been freed. This occurs when the first two
child pages of a parent both have deletions and the first page
has 1 remaining children and the second page has 2 remaining
children. During rebalancing the first page pulls an element from
the second page and then the second page pulls the same element
back from the first. The child page was not being freed properly.

I resolved this issue by removing this part of the rebalancing.
I made this choice for two reasons:

1. Moving a single item between pages has negligible benefit. The
   page will eventually be cleaned up when it reaches zero elements.

2. This is an infrequently executed branch of code which increases
   the likelihood of bugs occurring and it makes it more difficult
   to test properly.

Fixes #348
master
Ben Johnson 2016-03-21 08:53:28 -06:00
parent 7cb1534948
commit c0934840fd
1 changed files with 0 additions and 37 deletions

37
node.go
View File

@ -463,43 +463,6 @@ func (n *node) rebalance() {
target = n.prevSibling() target = n.prevSibling()
} }
// If target node has extra nodes then just move one over.
if target.numChildren() > target.minKeys() {
if useNextSibling {
// Reparent and move node.
if child, ok := n.bucket.nodes[target.inodes[0].pgid]; ok {
child.parent.removeChild(child)
child.parent = n
child.parent.children = append(child.parent.children, child)
}
n.inodes = append(n.inodes, target.inodes[0])
target.inodes = target.inodes[1:]
// Update target key on parent.
target.parent.put(target.key, target.inodes[0].key, nil, target.pgid, 0)
target.key = target.inodes[0].key
_assert(len(target.key) > 0, "rebalance(1): zero-length node key")
} else {
// Reparent and move node.
if child, ok := n.bucket.nodes[target.inodes[len(target.inodes)-1].pgid]; ok {
child.parent.removeChild(child)
child.parent = n
child.parent.children = append(child.parent.children, child)
}
n.inodes = append(n.inodes, inode{})
copy(n.inodes[1:], n.inodes)
n.inodes[0] = target.inodes[len(target.inodes)-1]
target.inodes = target.inodes[:len(target.inodes)-1]
}
// Update parent key for node.
n.parent.put(n.key, n.inodes[0].key, nil, n.pgid, 0)
n.key = n.inodes[0].key
_assert(len(n.key) > 0, "rebalance(2): zero-length node key")
return
}
// If both this node and the target node are too small then merge them. // If both this node and the target node are too small then merge them.
if useNextSibling { if useNextSibling {
// Reparent all child nodes being moved. // Reparent all child nodes being moved.