Ejemplo n.º 1
0
        private IEnumerable <KeyValuePair <byte[], ulong> > MakeInternalNodes(ulong txnId, IEnumerable <KeyValuePair <byte[], ulong> > children, BrightstarProfiler profiler)
        {
            var enumerator = children.GetEnumerator();
            var childList  = enumerator.Next(_internalBranchFactor).ToList();

            if (childList.Count == 1)
            {
                yield return(childList[0]);

                yield break;
            }

            byte[]        prevNodeKey = childList[0].Key;
            IInternalNode prevNode    = MakeInternalNode(txnId, childList);

            childList = enumerator.Next(_internalBranchFactor).ToList();
            while (childList.Count > 0)
            {
                IInternalNode nextNode    = MakeInternalNode(txnId, childList);
                var           nextNodeKey = childList[0].Key;
                if (nextNode.NeedJoin)
                {
                    nextNodeKey = new byte[_config.KeySize];
                    nextNode.RedistributeFromLeft(txnId, prevNode, childList[0].Key, nextNodeKey);
                }
                yield return(WriteNode(txnId, prevNode, prevNodeKey, profiler));

                prevNode    = nextNode;
                prevNodeKey = nextNodeKey;
                childList   = enumerator.Next(_internalBranchFactor).ToList();
            }

            yield return(WriteNode(txnId, prevNode, prevNodeKey, profiler));
        }
Ejemplo n.º 2
0
        internal IEnumerable <INode <T> > GetDescendants(IInternalNode <T> startNode, Func <IInternalNode <T>, bool> predicate)
        {
            ComputeLevels();
            var visitedNodes = new HashSet <IInternalNode <T> >();
            var stack        = new Stack <IInternalNode <T> >();

            stack.Push(startNode);
            while (stack.Count != 0)
            {
                var node = stack.Pop();
                visitedNodes.Add(node);
                if (predicate(node))
                {
                    yield return(node);
                }

                foreach (var child in node.Edges)
                {
                    if (visitedNodes.Contains(child))
                    {
                        continue;
                    }

                    stack.Push(child);
                }
            }
        }
Ejemplo n.º 3
0
        internal IEnumerable <INode <T> > GetPrecedents(IInternalNode <T> startNode, Func <IInternalNode <T>, bool> predicate)
        {
            this.ComputeLevels();
            var visitedNodes = new HashSet <IInternalNode <T> >();
            var stack        = new Stack <IInternalNode <T> >();

            stack.Push(startNode);
            while (stack.Count != 0)
            {
                var node = stack.Pop();
                visitedNodes.Add(node);
                if (predicate(node))
                {
                    yield return(node);
                }
                foreach (var parent in node.Parents)
                {
                    if (visitedNodes.Contains(parent))
                    {
                        continue;
                    }
                    stack.Push(parent);
                }
            }
        }
Ejemplo n.º 4
0
        public bool RedistributeFromRight(ulong txnId, IInternalNode rightSibling, byte[] joinKey, byte[] newJoinKey)
        {
            var right = rightSibling as InternalNode;

            if (right == null)
            {
                throw new ArgumentException("Expected a DirectInternalNode as right sibling", "rightSibling");
            }

            int required = _config.InternalSplitIndex - _keyCount;

            if (rightSibling.KeyCount - required < _config.InternalSplitIndex)
            {
                return(false);
            }

            EnsureWriteable(txnId);
            right.EnsureWriteable(txnId);

            // Copy keys and child pointers
            _page.SetData(joinKey, 0, KeyOffset(KeyCount), _config.KeySize); // Set key[KeyCount+1] to joinKey
            _page.SetData(right.GetData(), KeyOffset(0),
                          KeyOffset(KeyCount + 1), (required - 1) * _config.KeySize);
            _page.SetData(right.GetData(), PointerOffset(0),
                          PointerOffset(KeyCount + 1), required * 8);
            Array.Copy(right.GetData(), KeyOffset(required - 1),
                       newJoinKey, 0, _config.KeySize);
            right.LeftShift(required);
            KeyCount += required;
            return(true);
        }
Ejemplo n.º 5
0
        internal IEnumerable <INode <T> > GetDescendants(IInternalNode <T> startNode, Func <IInternalNode <T>, bool> predicate)
        {
            ComputeLevels();
            var visitedNodes = new bool[_nodes.Count];
            var stack        = new Stack <InternalNodeFast <T> >();

            stack.Push(startNode as InternalNodeFast <T>);
            while (stack.Count != 0)
            {
                var node = stack.Pop();
                visitedNodes[node.Key] = true;

                if (predicate(node))
                {
                    yield return(node);
                }

                foreach (var child in node.Edges)
                {
                    if (visitedNodes[child.Key])
                    {
                        continue;
                    }

                    stack.Push(child);
                }
            }
        }
Ejemplo n.º 6
0
        internal IEnumerable <INode <T> > GetPrecedents(IInternalNode <T> startNode, Func <IInternalNode <T>, bool> predicate)
        {
            this.ComputeLevels();
            var visitedNodes = new bool[this._nodes.Count];
            var stack        = new Stack <InternalNodeFast <T> >();

            stack.Push(startNode as InternalNodeFast <T>);
            while (stack.Count != 0)
            {
                var node = stack.Pop();
                visitedNodes[node.Key] = true;
                if (predicate(node))
                {
                    yield return(node);
                }
                foreach (var parent in node.Parents)
                {
                    if (visitedNodes[parent.Key])
                    {
                        continue;
                    }
                    stack.Push(parent);
                }
            }
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Attempts to ensure that the minimum size for this node is achieved by transferring entries from the left-hand sibling
        /// </summary>
        /// <param name="txnId"></param>
        /// <param name="leftSibling">The left-hand sibling that will provide entries</param>
        /// <param name="joinKey">The value of the key that is present in the parent node between the pointer to this node and its left sibling</param>
        /// <param name="newJoinKey">The replacement value for the join key in the parent node</param>
        /// <returns>True if the node achieves its minimum size by the redistribution process, false otherwise</returns>
        public bool RedistributeFromLeft(ulong txnId, IInternalNode leftSibling, byte[] joinKey, byte[] newJoinKey)
        {
            InternalNode left = leftSibling as InternalNode;

            if (left == null)
            {
                throw new ArgumentException("Expected a InternalNode as left sibling", "leftSibling");
            }

            int required = _config.InternalSplitIndex - KeyCount;

            if (leftSibling.KeyCount - required < _config.InternalSplitIndex)
            {
                // Can't fulfill requirements with a borrow from left sibling
                return(false);
            }

            int evenOut = (KeyCount + left.KeyCount) / 2 - KeyCount;

            if (leftSibling.KeyCount - evenOut > _config.InternalSplitIndex)
            {
                required = evenOut;
            }

            EnsureWriteable(txnId);
            left.EnsureWriteable(txnId);

            // Make space for new keys and child pointers
            RightShift(required);

            _page.SetData(joinKey, 0, KeyOffset(required - 1), _config.KeySize);
            _page.SetData(left.GetData(), KeyOffset(left.KeyCount - (required - 1)),
                          KeyOffset(0), (required - 1) * _config.KeySize);
            _page.SetData(left.GetData(), PointerOffset(left.KeyCount - (required - 1)),
                          PointerOffset(0), (required) * 8);
            Array.Copy(left.GetData(), KeyOffset(left.KeyCount - required), newJoinKey, 0, _config.KeySize);
            KeyCount      += required;
            left.KeyCount -= required;
            return(true);
        }
Ejemplo n.º 8
0
 private KeyValuePair<byte[], ulong > WriteNode(ulong  txnId, IInternalNode node, byte[] lowestLeafKey, BrightstarProfiler profiler)
 {
     //_pageStore.Write(txnId, node.PageId, node.GetData(), profiler:profiler);
     return new KeyValuePair<byte[], ulong>(lowestLeafKey, node.PageId);
 }
Ejemplo n.º 9
0
 private KeyValuePair <byte[], ulong> WriteNode(ulong txnId, IInternalNode node, byte[] lowestLeafKey, BrightstarProfiler profiler)
 {
     //_pageStore.Write(txnId, node.PageId, node.GetData(), profiler:profiler);
     return(new KeyValuePair <byte[], ulong>(lowestLeafKey, node.PageId));
 }
Ejemplo n.º 10
0
        private void Delete(ulong txnId, IInternalNode parentInternalNode, byte[] key, out bool underAllocation, BrightstarProfiler profiler)
        {
            if (parentInternalNode.RightmostKey == null)
            {
                throw new ArgumentException("Parent node right key is null");
            }
            var childNodeId = parentInternalNode.GetChildNodeId(key);
            var childNode   = GetNode(childNodeId, profiler);

            if (childNode is ILeafNode)
            {
                var childLeafNode = childNode as ILeafNode;
                // Delete the key and mark the node as updated. This may update the child node id
                childLeafNode.Delete(txnId, key);
                MarkDirty(txnId, childLeafNode, profiler);
                if (childLeafNode.PageId != childNodeId)
                {
                    parentInternalNode.UpdateChildPointer(txnId, childNodeId, childLeafNode.PageId);
                    childNodeId = childLeafNode.PageId;
                }

                if (childLeafNode.NeedsJoin)
                {
                    ulong     leftSiblingId, rightSiblingId;
                    ILeafNode leftSibling = null, rightSibling = null;
                    bool      hasLeftSibling = parentInternalNode.GetLeftSibling(childNodeId, out leftSiblingId);
                    if (hasLeftSibling)
                    {
                        leftSibling = GetNode(leftSiblingId, profiler) as ILeafNode;
                        if (childLeafNode.RedistributeFromLeft(txnId, leftSibling))
                        {
                            parentInternalNode.SetLeftKey(txnId, childLeafNode.PageId, childLeafNode.LeftmostKey);
                            MarkDirty(txnId, parentInternalNode, profiler);
                            MarkDirty(txnId, leftSibling, null);
                            parentInternalNode.UpdateChildPointer(txnId, leftSiblingId, leftSibling.PageId);
                            underAllocation = false;
                            return;
                        }
                    }
                    bool hasRightSibling = parentInternalNode.GetRightSiblingId(childNodeId, out rightSiblingId);


                    if (hasRightSibling)
                    {
                        rightSibling = GetNode(rightSiblingId, profiler) as ILeafNode;
#if DEBUG
                        if (rightSibling.LeftmostKey.Compare(childLeafNode.RightmostKey) <= 0)
                        {
                            throw new Exception("Right-hand sibling has a left key lower than this nodes right key.");
                        }
#endif
                        if (childLeafNode.RedistributeFromRight(txnId, rightSibling))
                        {
                            MarkDirty(txnId, rightSibling, profiler);
                            parentInternalNode.UpdateChildPointer(txnId, rightSiblingId, rightSibling.PageId);
                            parentInternalNode.SetLeftKey(txnId, rightSibling.PageId, rightSibling.LeftmostKey);
                            MarkDirty(txnId, parentInternalNode, profiler);
                            underAllocation = false;
                            return;
                        }
                    }
                    if (hasLeftSibling && childLeafNode.Merge(txnId, leftSibling))
                    {
                        parentInternalNode.RemoveChildPointer(txnId, leftSiblingId);
                        parentInternalNode.SetLeftKey(txnId, childLeafNode.PageId, childLeafNode.LeftmostKey);
                        MarkDirty(txnId, parentInternalNode, profiler);
                        underAllocation = parentInternalNode.NeedJoin;
                        return;
                    }
                    if (hasRightSibling && childLeafNode.Merge(txnId, rightSibling))
                    {
                        byte[] nodeKey = parentInternalNode.RemoveChildPointer(txnId, rightSiblingId);
                        if (nodeKey == null)
                        {
                            // We merged in the right-most node, so we need to generate a key
                            nodeKey = new byte[_config.KeySize];
                            Array.Copy(rightSibling.RightmostKey, nodeKey, _config.KeySize);
                            ByteArrayHelper.Increment(nodeKey);
                        }
                        parentInternalNode.SetKey(txnId, childLeafNode.PageId, nodeKey);
                        MarkDirty(txnId, parentInternalNode, profiler);
                        underAllocation = parentInternalNode.NeedJoin;
                        return;
                    }
                }
                underAllocation = false;
                return;
            }


            if (childNode is IInternalNode)
            {
                bool childUnderAllocated;
                var  childInternalNode = childNode as IInternalNode;
                Delete(txnId, childInternalNode, key, out childUnderAllocated, profiler);
                if (childInternalNode.PageId != childNodeId)
                {
                    // Child node page changed
                    parentInternalNode.UpdateChildPointer(txnId, childNodeId, childInternalNode.PageId);
                    MarkDirty(txnId, parentInternalNode, profiler);
                    childNodeId = childInternalNode.PageId;
                }

                if (childUnderAllocated)
                {
                    IInternalNode leftSibling = null, rightSibling = null;
                    ulong         leftSiblingId, rightSiblingId;

                    // Redistribute values from left-hand sibling
                    bool hasLeftSibling = parentInternalNode.GetLeftSibling(childNodeId, out leftSiblingId);
                    if (hasLeftSibling)
                    {
                        leftSibling = GetNode(leftSiblingId, profiler) as IInternalNode;
                        byte[] joinKey    = parentInternalNode.GetKey(leftSiblingId);
                        var    newJoinKey = new byte[_config.KeySize];
                        if (childInternalNode.RedistributeFromLeft(txnId, leftSibling, joinKey, newJoinKey))
                        {
                            MarkDirty(txnId, leftSibling, profiler);
                            parentInternalNode.UpdateChildPointer(txnId, leftSiblingId, leftSibling.PageId);
                            parentInternalNode.SetKey(txnId, leftSibling.PageId, newJoinKey);
                            MarkDirty(txnId, parentInternalNode, profiler);
                            underAllocation = false;
                            return;
                        }
                    }

                    // Redistribute values from right-hand sibling
                    bool hasRightSibling = parentInternalNode.GetRightSiblingId(childNodeId, out rightSiblingId);
                    if (hasRightSibling)
                    {
                        rightSibling = GetNode(rightSiblingId, profiler) as IInternalNode;
                        byte[] joinKey    = parentInternalNode.GetKey(childInternalNode.PageId);
                        byte[] newJoinKey = new byte[_config.KeySize];
                        if (childInternalNode.RedistributeFromRight(txnId, rightSibling, joinKey, newJoinKey))
                        {
                            MarkDirty(txnId, rightSibling, profiler);
                            parentInternalNode.UpdateChildPointer(txnId, rightSiblingId, rightSibling.PageId);
                            // parentInternalNode.SetKey(rightSibling.PageId, newJoinKey); -- think this is wrong should be:
                            parentInternalNode.SetKey(txnId, childInternalNode.PageId, newJoinKey);
                            MarkDirty(txnId, parentInternalNode, profiler);
                            underAllocation = false;
                            return;
                        }
                    }

                    // Merge with left-hand sibling
                    if (hasLeftSibling)
                    {
                        // Attempt to merge child node into its left sibling
                        var joinKey       = parentInternalNode.GetKey(leftSibling.PageId);
                        var mergedNodeKey = parentInternalNode.GetKey(childInternalNode.PageId);
                        if (mergedNodeKey == null)
                        {
                            mergedNodeKey = new byte[_config.KeySize];
                            Array.Copy(childInternalNode.RightmostKey, mergedNodeKey, _config.KeySize);
                            ByteArrayHelper.Increment(mergedNodeKey);
                        }
                        if (leftSibling.Merge(txnId, childInternalNode, joinKey))
                        {
                            MarkDirty(txnId, leftSibling, profiler);
                            if (leftSibling.PageId != leftSiblingId)
                            {
                                // We have a new page id (append-only stores will do this)
                                parentInternalNode.UpdateChildPointer(txnId, leftSiblingId, leftSibling.PageId);
                            }
                            parentInternalNode.RemoveChildPointer(txnId, childInternalNode.PageId);
                            parentInternalNode.SetKey(txnId, leftSibling.PageId, mergedNodeKey);
                            MarkDirty(txnId, parentInternalNode, profiler);
                            underAllocation = parentInternalNode.NeedJoin;
                            return;
                        }
                    }

                    // Merge with right-hand sibling
                    if (hasRightSibling)
                    {
                        // Attempt to merge right sibling into child node
                        var joinKey = parentInternalNode.GetKey(childNodeId);
                        if (childInternalNode.Merge(txnId, rightSibling, joinKey))
                        {
                            MarkDirty(txnId, childInternalNode, profiler);
                            var nodeKey = parentInternalNode.RemoveChildPointer(txnId, rightSiblingId);
                            if (childInternalNode.PageId != childNodeId)
                            {
                                // We have a new page id for the child node (append-only stores will do this)
                                parentInternalNode.UpdateChildPointer(txnId, childNodeId, childInternalNode.PageId);
                            }
                            if (nodeKey == null)
                            {
                                // We merged in the right-most node, so we need to generate a key
                                nodeKey = new byte[_config.KeySize];
                                Array.Copy(rightSibling.RightmostKey, nodeKey, _config.KeySize);
                                ByteArrayHelper.Increment(nodeKey);
                            }
                            parentInternalNode.SetKey(txnId, childInternalNode.PageId, nodeKey);
                            MarkDirty(txnId, parentInternalNode, profiler);
                            underAllocation = parentInternalNode.NeedJoin;
                            return;
                        }
                    }

                    throw new NotImplementedException(
                              "Not yet implemented handling for internal node becoming under allocated");
                }
                underAllocation = false;
            }
            else
            {
                throw new BrightstarInternalException(String.Format("Unrecognised B+ Tree node class : {0}",
                                                                    childNode.GetType()));
            }
        }
Ejemplo n.º 11
0
        public bool RedistributeFromRight(ulong txnId, IInternalNode rightSibling, byte[] joinKey, byte[] newJoinKey)
        {
            var right = rightSibling as InternalNode;
            if (right == null) throw new ArgumentException("Expected a DirectInternalNode as right sibling", "rightSibling");

            int required = _config.InternalSplitIndex - _keyCount;
            if (rightSibling.KeyCount - required < _config.InternalSplitIndex)
            {
                return false;
            }

            EnsureWriteable(txnId);
            right.EnsureWriteable(txnId);

            // Copy keys and child pointers
            _page.SetData(joinKey, 0, KeyOffset(KeyCount), _config.KeySize); // Set key[KeyCount+1] to joinKey
            _page.SetData(right.GetData(), KeyOffset(0),
                          KeyOffset(KeyCount + 1), (required - 1)*_config.KeySize);
            _page.SetData(right.GetData(), PointerOffset(0),
                          PointerOffset(KeyCount + 1), required*8);
            Array.Copy(right.GetData(), KeyOffset(required - 1),
                       newJoinKey, 0, _config.KeySize);
            right.LeftShift(required);
            KeyCount += required;
            return true;
        }
Ejemplo n.º 12
0
        /// <summary>
        /// Attempts to ensure that the minimum size for this node is achieved by transferring entries from the left-hand sibling
        /// </summary>
        /// <param name="txnId"></param>
        /// <param name="leftSibling">The left-hand sibling that will provide entries</param>
        /// <param name="joinKey">The value of the key that is present in the parent node between the pointer to this node and its left sibling</param>
        /// <param name="newJoinKey">The replacement value for the join key in the parent node</param>
        /// <returns>True if the node achieves its minimum size by the redistribution process, false otherwise</returns>
        public bool RedistributeFromLeft(ulong txnId, IInternalNode leftSibling, byte[] joinKey, byte[] newJoinKey)
        {
            InternalNode left = leftSibling as InternalNode;
            if (left == null)
            {
                throw new ArgumentException("Expected a InternalNode as left sibling", "leftSibling");
            }

            int required = _config.InternalSplitIndex - KeyCount;
            if (leftSibling.KeyCount - required < _config.InternalSplitIndex)
            {
                // Can't fulfill requirements with a borrow from left sibling
                return false;
            }

            int evenOut = (KeyCount + left.KeyCount)/2 - KeyCount;
            if (leftSibling.KeyCount - evenOut > _config.InternalSplitIndex)
            {
                required = evenOut;
            }

            EnsureWriteable(txnId);
            left.EnsureWriteable(txnId);

            // Make space for new keys and child pointers
            RightShift(required);

            _page.SetData(joinKey, 0, KeyOffset(required - 1), _config.KeySize);
            _page.SetData(left.GetData(), KeyOffset(left.KeyCount - (required - 1)),
                          KeyOffset(0), (required - 1)*_config.KeySize);
            _page.SetData(left.GetData(), PointerOffset(left.KeyCount - (required - 1)),
                          PointerOffset(0), (required)*8);
            Array.Copy(left.GetData(), KeyOffset(left.KeyCount-required), newJoinKey, 0, _config.KeySize);
            KeyCount += required;
            left.KeyCount -= required;
            return true;
        }
Ejemplo n.º 13
0
        private void Delete(ulong txnId, IInternalNode parentInternalNode, byte[] key, out bool underAllocation, BrightstarProfiler profiler)
        {
            if (parentInternalNode.RightmostKey == null)
            {
                throw new ArgumentException("Parent node right key is null");
            }
            var childNodeId = parentInternalNode.GetChildNodeId(key);
            var childNode = GetNode(childNodeId, profiler);
            if (childNode is ILeafNode)
            {
                var childLeafNode = childNode as ILeafNode;
                // Delete the key and mark the node as updated. This may update the child node id
                childLeafNode.Delete(txnId, key);
                MarkDirty(txnId, childLeafNode, profiler);
                if (childLeafNode.PageId != childNodeId)
                {
                    parentInternalNode.UpdateChildPointer(txnId, childNodeId, childLeafNode.PageId);
                    childNodeId = childLeafNode.PageId;
                }

                if (childLeafNode.NeedsJoin)
                {
                    ulong leftSiblingId, rightSiblingId;
                    ILeafNode leftSibling = null, rightSibling = null;
                    bool hasLeftSibling = parentInternalNode.GetLeftSibling(childNodeId, out leftSiblingId);
                    if (hasLeftSibling)
                    {
                        leftSibling = GetNode(leftSiblingId, profiler) as ILeafNode;
                        if (childLeafNode.RedistributeFromLeft(txnId, leftSibling))
                        {
                            parentInternalNode.SetLeftKey(txnId, childLeafNode.PageId, childLeafNode.LeftmostKey);
                            MarkDirty(txnId, parentInternalNode, profiler);
                            MarkDirty(txnId, leftSibling, null);
                            parentInternalNode.UpdateChildPointer(txnId, leftSiblingId, leftSibling.PageId);
                            underAllocation = false;
                            return;
                        }
                    }
                    bool hasRightSibling = parentInternalNode.GetRightSiblingId(childNodeId, out rightSiblingId);
                    
                        
                    if (hasRightSibling)
                    {
                        rightSibling = GetNode(rightSiblingId, profiler) as ILeafNode;
#if DEBUG
                        if (rightSibling.LeftmostKey.Compare(childLeafNode.RightmostKey) <= 0)
                        {
                            throw new Exception("Right-hand sibling has a left key lower than this nodes right key.");
                        }
#endif
                        if (childLeafNode.RedistributeFromRight(txnId, rightSibling))
                        {
                            MarkDirty(txnId, rightSibling, profiler);
                            parentInternalNode.UpdateChildPointer(txnId, rightSiblingId, rightSibling.PageId);
                            parentInternalNode.SetLeftKey(txnId, rightSibling.PageId, rightSibling.LeftmostKey);
                            MarkDirty(txnId, parentInternalNode, profiler);
                            underAllocation = false;
                            return;
                        }
                    }
                    if (hasLeftSibling && childLeafNode.Merge(txnId, leftSibling))
                    {
                        parentInternalNode.RemoveChildPointer(txnId, leftSiblingId);
                        parentInternalNode.SetLeftKey(txnId, childLeafNode.PageId, childLeafNode.LeftmostKey);
                        MarkDirty(txnId, parentInternalNode, profiler);
                        underAllocation = parentInternalNode.NeedJoin;
                        return;
                    }
                    if (hasRightSibling && childLeafNode.Merge(txnId, rightSibling))
                    {
                        byte[] nodeKey = parentInternalNode.RemoveChildPointer(txnId, rightSiblingId);
                        if (nodeKey == null)
                        {
                            // We merged in the right-most node, so we need to generate a key
                            nodeKey = new byte[_config.KeySize];
                            Array.Copy(rightSibling.RightmostKey, nodeKey, _config.KeySize);
                            ByteArrayHelper.Increment(nodeKey);
                        }
                        parentInternalNode.SetKey(txnId, childLeafNode.PageId, nodeKey);
                        MarkDirty(txnId, parentInternalNode, profiler);
                        underAllocation = parentInternalNode.NeedJoin;
                        return;
                    }
                }
                underAllocation = false;
                return;
            }


            if (childNode is IInternalNode)
            {
                bool childUnderAllocated;
                var childInternalNode = childNode as IInternalNode;
                Delete(txnId, childInternalNode, key, out childUnderAllocated, profiler);
                if (childInternalNode.PageId != childNodeId)
                {
                    // Child node page changed
                    parentInternalNode.UpdateChildPointer(txnId, childNodeId, childInternalNode.PageId);
                    MarkDirty(txnId, parentInternalNode, profiler);
                    childNodeId = childInternalNode.PageId;
                }

                if (childUnderAllocated)
                {
                    IInternalNode leftSibling = null, rightSibling = null;
                    ulong leftSiblingId, rightSiblingId;

                    // Redistribute values from left-hand sibling
                    bool hasLeftSibling = parentInternalNode.GetLeftSibling(childNodeId, out leftSiblingId);
                    if (hasLeftSibling)
                    {
                        leftSibling = GetNode(leftSiblingId, profiler) as IInternalNode;
                        byte[] joinKey = parentInternalNode.GetKey(leftSiblingId);
                        var newJoinKey = new byte[_config.KeySize];
                        if (childInternalNode.RedistributeFromLeft(txnId, leftSibling, joinKey, newJoinKey))
                        {
                            MarkDirty(txnId, leftSibling, profiler);
                            parentInternalNode.UpdateChildPointer(txnId, leftSiblingId, leftSibling.PageId);
                            parentInternalNode.SetKey(txnId, leftSibling.PageId, newJoinKey);
                            MarkDirty(txnId, parentInternalNode, profiler);
                            underAllocation = false;
                            return;
                        }
                    }

                    // Redistribute values from right-hand sibling
                    bool hasRightSibling = parentInternalNode.GetRightSiblingId(childNodeId, out rightSiblingId);
                    if (hasRightSibling)
                    {
                        rightSibling = GetNode(rightSiblingId, profiler) as IInternalNode;
                        byte[] joinKey = parentInternalNode.GetKey(childInternalNode.PageId);
                        byte[] newJoinKey = new byte[_config.KeySize];
                        if (childInternalNode.RedistributeFromRight(txnId, rightSibling, joinKey, newJoinKey))
                        {
                            MarkDirty(txnId, rightSibling, profiler);
                            parentInternalNode.UpdateChildPointer(txnId, rightSiblingId, rightSibling.PageId);
                            // parentInternalNode.SetKey(rightSibling.PageId, newJoinKey); -- think this is wrong should be:
                            parentInternalNode.SetKey(txnId, childInternalNode.PageId, newJoinKey);
                            MarkDirty(txnId, parentInternalNode, profiler);
                            underAllocation = false;
                            return;
                        }
                    }

                    // Merge with left-hand sibling
                    if (hasLeftSibling)
                    {
                        // Attempt to merge child node into its left sibling
                        var joinKey = parentInternalNode.GetKey(leftSibling.PageId);
                        var mergedNodeKey = parentInternalNode.GetKey(childInternalNode.PageId);
                        if (mergedNodeKey == null)
                        {
                            mergedNodeKey = new byte[_config.KeySize];
                            Array.Copy(childInternalNode.RightmostKey, mergedNodeKey, _config.KeySize);
                            ByteArrayHelper.Increment(mergedNodeKey);
                        }
                        if (leftSibling.Merge(txnId, childInternalNode, joinKey))
                        {
                            MarkDirty(txnId, leftSibling, profiler);
                            if (leftSibling.PageId != leftSiblingId)
                            {
                                // We have a new page id (append-only stores will do this)
                                parentInternalNode.UpdateChildPointer(txnId, leftSiblingId, leftSibling.PageId);
                            }
                            parentInternalNode.RemoveChildPointer(txnId, childInternalNode.PageId);
                            parentInternalNode.SetKey(txnId, leftSibling.PageId, mergedNodeKey);
                            MarkDirty(txnId, parentInternalNode, profiler);
                            underAllocation = parentInternalNode.NeedJoin;
                            return;
                        }
                    }

                    // Merge with right-hand sibling
                    if (hasRightSibling)
                    {
                        // Attempt to merge right sibling into child node
                        var joinKey = parentInternalNode.GetKey(childNodeId);
                        if (childInternalNode.Merge(txnId, rightSibling, joinKey))
                        {
                            MarkDirty(txnId, childInternalNode, profiler);
                            var nodeKey = parentInternalNode.RemoveChildPointer(txnId, rightSiblingId);
                            if (childInternalNode.PageId != childNodeId)
                            {
                                // We have a new page id for the child node (append-only stores will do this)
                                parentInternalNode.UpdateChildPointer(txnId, childNodeId, childInternalNode.PageId);
                            }
                            if (nodeKey == null)
                            {
                                // We merged in the right-most node, so we need to generate a key
                                nodeKey = new byte[_config.KeySize];
                                Array.Copy(rightSibling.RightmostKey, nodeKey, _config.KeySize);
                                ByteArrayHelper.Increment(nodeKey);
                            }
                            parentInternalNode.SetKey(txnId, childInternalNode.PageId, nodeKey);
                            MarkDirty(txnId, parentInternalNode, profiler);
                            underAllocation = parentInternalNode.NeedJoin;
                            return;
                        }
                    }

                    throw new NotImplementedException(
                        "Not yet implemented handling for internal node becoming under allocated");
                }
                underAllocation = false;
            }
            else
            {
                throw new BrightstarInternalException(String.Format("Unrecognised B+ Tree node class : {0}",
                                                                    childNode.GetType()));
            }
        }