From c0934840fdd3462df8ea9f408f791a65957598ac Mon Sep 17 00:00:00 2001 From: Ben Johnson Date: Mon, 21 Mar 2016 08:53:28 -0600 Subject: [PATCH] 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 --- node.go | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/node.go b/node.go index c9fb21c..e9d64af 100644 --- a/node.go +++ b/node.go @@ -463,43 +463,6 @@ func (n *node) rebalance() { 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 useNextSibling { // Reparent all child nodes being moved.