Ejemplo n.º 1
0
        public void TestBorrowLeft()
        {
            var config = new BPlusTreeConfiguration(8, 8, 4096);

            var leftNode = new LeafNode(1ul, 0ul, 0ul, config);
            var rightNode = new LeafNode(2ul, 0ul, 0ul, config);
            byte[] testBytes = new byte[]{1,2,3,4};
            leftNode.Insert(BitConverter.GetBytes(1ul), testBytes);
            leftNode.Insert(BitConverter.GetBytes(2ul), testBytes); 
            leftNode.Insert(BitConverter.GetBytes(3ul), testBytes); 
            leftNode.Insert(BitConverter.GetBytes(4ul), testBytes); 
            leftNode.Insert(BitConverter.GetBytes(5ul), testBytes);

            rightNode.Insert(BitConverter.GetBytes(10ul), testBytes);
            rightNode.Insert(BitConverter.GetBytes(11ul), testBytes);

            Assert.IsTrue(rightNode.RedistributeFromLeft(leftNode));
            Assert.IsTrue(rightNode.KeyCount == 3);
            
            TestUtils.AssertBuffersEqual(rightNode.LeftmostKey, BitConverter.GetBytes(5ul));
            Assert.IsTrue(leftNode.KeyCount == 4);
            TestUtils.AssertBuffersEqual(leftNode.RightmostKey, BitConverter.GetBytes(4ul));

            leftNode.Delete(BitConverter.GetBytes(1ul));
            leftNode.Delete(BitConverter.GetBytes(2ul));
            Assert.IsFalse(rightNode.RedistributeFromLeft(leftNode));
        }
Ejemplo n.º 2
0
        public void TestBorrowRight()
        {
            var pageStore = new MemoryPageStore(4096);
            var config = new BPlusTreeConfiguration(pageStore, 8, 8, 4096);
            var leftNode = new LeafNode(pageStore.Create(0), 0, 0, config);
            var rightNode = new LeafNode(pageStore.Create(0), 0, 0, config);

            byte[] testBytes = new byte[] { 1, 2, 3, 4 };

            leftNode.Insert(1, BitConverter.GetBytes(1ul), testBytes);
            leftNode.Insert(1, BitConverter.GetBytes(2ul), testBytes);

            rightNode.Insert(1, BitConverter.GetBytes(10ul), testBytes);
            rightNode.Insert(1, BitConverter.GetBytes(11ul), testBytes);
            rightNode.Insert(1, BitConverter.GetBytes(12ul), testBytes);
            rightNode.Insert(1, BitConverter.GetBytes(13ul), testBytes);
            rightNode.Insert(1, BitConverter.GetBytes(14ul), testBytes);

            Assert.IsTrue(leftNode.RedistributeFromRight(1, rightNode));
            Assert.IsTrue(leftNode.KeyCount == 3);
            TestUtils.AssertBuffersEqual(BitConverter.GetBytes(10ul), leftNode.RightmostKey);
            TestUtils.AssertBuffersEqual(BitConverter.GetBytes(11ul), rightNode.LeftmostKey);
            Assert.IsTrue(rightNode.KeyCount == 4);
            Assert.IsFalse(leftNode.RedistributeFromRight(1, rightNode));
        }
Ejemplo n.º 3
0
        public void TestLeafNodeSplit()
        {
            var pageStore = new MemoryPageStore(4096);
            var config = new BPlusTreeConfiguration(pageStore, 8, 8, 4096);
            var firstNode = new LeafNode(pageStore.Create(0), 0, 0, config);
            var testBytes = new byte[]{1,2,3,4,5,6,7,8};
            for (int i = 0; i < config.LeafLoadFactor; i++)
            {
                firstNode.Insert(0, BitConverter.GetBytes((ulong)i), testBytes);
            }

            var secondNodePage = pageStore.Create(0);
            var splitKey = new byte[8];
            var secondNode = firstNode.Split(0, secondNodePage, out splitKey);

            Assert.AreEqual(config.LeafSplitIndex, firstNode.KeyCount);
            Assert.AreEqual(config.LeafLoadFactor - config.LeafSplitIndex, secondNode.KeyCount);

            var firstNodePage = pageStore.Retrieve(0, null);
            var firstNodeKeyCount = BitConverter.ToInt32(firstNodePage.Data, 0);
            secondNodePage = pageStore.Retrieve(1, null);
            var secondNodeKeyCount = BitConverter.ToInt32(secondNodePage.Data, 0);
            Assert.AreEqual(config.LeafSplitIndex, firstNodeKeyCount);
            Assert.AreEqual(config.LeafLoadFactor - config.LeafSplitIndex, secondNodeKeyCount);
            
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Split this leaf node into two
        /// </summary>
        /// <param name="newNodeId">The ID of the page reserved to store the new node</param>
        /// <param name="splitKey">Receives the key that was used for the split</param>
        /// <returns>The new node created by the split</returns>
        /// <remarks>The split operation always creates a new node for the upper (right-hand) half of the keys and keeps the lower (left-hand) half in this leaf node.</remarks>
        public LeafNode Split(ulong newNodeId, out byte[] splitKey)
        {
            var rightNode = new LeafNode(newNodeId, PageId, _nextPointer, _config);

            _nextPointer = newNodeId;
            splitKey     = new byte[_config.KeySize];
            Array.Copy(_keys[_config.LeafSplitIndex], splitKey, _config.KeySize);
            Array.Copy(_keys, _config.LeafSplitIndex, rightNode._keys, 0, _keyCount - _config.LeafSplitIndex);
            Array.Copy(_dataSegments, _config.LeafSplitIndex, rightNode._dataSegments, 0,
                       _keyCount - _config.LeafSplitIndex);
            rightNode._keyCount = _keyCount - _config.LeafSplitIndex;
            _keyCount           = _config.LeafSplitIndex;
            return(rightNode);
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Attempts to ensure that the minimum size for this node is achieved by transferring entries from the right-hand sibling
        /// </summary>
        /// <param name="rightNode">The right-hand sibling that will provide entries</param>
        /// <returns>True if the node achieves its minimum size by the redistribution process, false otherwise</returns>
        public bool RedistributeFromRight(LeafNode rightNode)
        {
            int copyCount = (_keyCount + rightNode._keyCount) / 2 - _keyCount;

            if (copyCount > 0)
            {
                // Copy keys and data segments from right node
                Array.Copy(rightNode._keys, 0, _keys, _keyCount, copyCount);
                Array.Copy(rightNode._dataSegments, 0, _dataSegments, _keyCount, copyCount);
                // Shift up the remaining keys and data segments in the right node
                for (int i = 0; i < rightNode._keyCount - copyCount; i++)
                {
                    rightNode._keys[i]         = rightNode._keys[i + copyCount];
                    rightNode._dataSegments[i] = rightNode._dataSegments[i + copyCount];
                }
                rightNode._keyCount -= copyCount;
                _keyCount           += copyCount;
                return(true);
            }
            return(false);
        }
Ejemplo n.º 6
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="leftNode">The left-hand sibling that will provide entries</param>
        /// <returns>True if the node achieves its minimum size by the redistribution process, false otherwise</returns>
        public bool RedistributeFromLeft(LeafNode leftNode)
        {
            int copyCount = (_keyCount + leftNode.KeyCount) / 2 - _keyCount;

            if (copyCount > 0)
            {
                // Move down my data segments to make room for the copied ones
                for (int i = _keyCount - 1; i >= 0; i--)
                {
                    _keys[i + copyCount]         = _keys[i];
                    _dataSegments[i + copyCount] = _dataSegments[i];
                }
                // Copy keys and data segments from left node
                Array.Copy(leftNode._keys, leftNode._keyCount - copyCount, _keys, 0, copyCount);
                Array.Copy(leftNode._dataSegments, leftNode._keyCount - copyCount, _dataSegments, 0, copyCount);
                _keyCount          += copyCount;
                leftNode._keyCount -= copyCount;
                return(true);
            }
            return(false);
        }
Ejemplo n.º 7
0
        public INode GetNode(ulong nodeId, BrightstarProfiler profiler)
        {
            using (profiler.Step("BPlusTree.GetNode"))
            {
                INode ret;
                if (_modifiedNodes.TryGetValue(nodeId, out ret))
                {
                    profiler.Incr("NodeCache Hit");
                    return ret;
                }
                if (_nodeCache.TryGetValue(nodeId, out ret))
                {
                    profiler.Incr("NodeCache Hit");
                    return ret;
                }

                profiler.Incr("NodeCache Miss");
                using (profiler.Step("Load Node"))
                {
                    var nodePage = _pageStore.Retrieve(nodeId, profiler);
                    var header = BitConverter.ToInt32(nodePage, 0);
                    if (header < 0)
                    {
                        ret = new InternalNode(nodeId, nodePage, ~header, _config);
                    }
                    else
                    {
                        ret = new LeafNode(nodeId, nodePage, header, _config);
                    }
                    _nodeCache.Add(ret);
                    return ret;
                }
            }
        }
Ejemplo n.º 8
0
        private void Delete(ulong txnId, InternalNode 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 LeafNode)
            {
                var childLeafNode = childNode as LeafNode;
                // Delete the key and mark the node as updated. This may update the child node id
                childLeafNode.Delete(key);
                MarkDirty(txnId, childLeafNode, profiler);
                if (childLeafNode.PageId != childNodeId)
                {
                    parentInternalNode.UpdateChildPointer(childNodeId, childLeafNode.PageId);
                    childNodeId = childLeafNode.PageId;
                }

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


                    if (hasRightSibling)
                    {
                        rightSibling = GetNode(rightSiblingId, profiler) as LeafNode;
#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(rightSibling))
                        {
                            MarkDirty(txnId, rightSibling, profiler);
                            parentInternalNode.UpdateChildPointer(rightSiblingId, rightSibling.PageId);
                            parentInternalNode.SetLeftKey(rightSibling.PageId, rightSibling.LeftmostKey);
                            MarkDirty(txnId, parentInternalNode, profiler);
                            underAllocation = false;
                            return;
                        }
                    }
                    if (hasLeftSibling && childLeafNode.Merge(leftSibling))
                    {
                        parentInternalNode.RemoveChildPointer(leftSiblingId);
                        parentInternalNode.SetLeftKey(childLeafNode.PageId, childLeafNode.LeftmostKey);
                        MarkDirty(txnId, parentInternalNode, profiler);
                        underAllocation = parentInternalNode.NeedJoin;
                        return;
                    }
                    if (hasRightSibling && childLeafNode.Merge(rightSibling))
                    {
                        byte[] nodeKey = parentInternalNode.RemoveChildPointer(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(childLeafNode.PageId, nodeKey);
                        MarkDirty(txnId, parentInternalNode, profiler);
                        underAllocation = parentInternalNode.NeedJoin;
                        return;
                    }
                }
                underAllocation = false;
                return;
            }


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

                if (childUnderAllocated)
                {
                    InternalNode 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 InternalNode;
                        byte[] joinKey    = parentInternalNode.GetKey(leftSiblingId);
                        var    newJoinKey = new byte[_config.KeySize];
                        if (childInternalNode.RedistributeFromLeft(leftSibling, joinKey, newJoinKey))
                        {
                            MarkDirty(txnId, leftSibling, profiler);
                            parentInternalNode.UpdateChildPointer(leftSiblingId, leftSibling.PageId);
                            parentInternalNode.SetKey(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 InternalNode;
                        byte[] joinKey    = parentInternalNode.GetKey(childInternalNode.PageId);
                        byte[] newJoinKey = new byte[_config.KeySize];
                        if (childInternalNode.RedistributeFromRight(rightSibling, joinKey, newJoinKey))
                        {
                            MarkDirty(txnId, rightSibling, profiler);
                            parentInternalNode.UpdateChildPointer(rightSiblingId, rightSibling.PageId);
                            // parentInternalNode.SetKey(rightSibling.PageId, newJoinKey); -- think this is wrong should be:
                            parentInternalNode.SetKey(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(childInternalNode, joinKey))
                        {
                            MarkDirty(txnId, leftSibling, profiler);
                            if (leftSibling.PageId != leftSiblingId)
                            {
                                // We have a new page id (append-only stores will do this)
                                parentInternalNode.UpdateChildPointer(leftSiblingId, leftSibling.PageId);
                            }
                            parentInternalNode.RemoveChildPointer(childInternalNode.PageId);
                            parentInternalNode.SetKey(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(rightSibling, joinKey))
                        {
                            MarkDirty(txnId, childInternalNode, profiler);
                            var nodeKey = parentInternalNode.RemoveChildPointer(rightSiblingId);
                            if (childInternalNode.PageId != childNodeId)
                            {
                                // We have a new page id for the child node (append-only stores will do this)
                                parentInternalNode.UpdateChildPointer(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(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.º 9
0
 private KeyValuePair <byte[], ulong> WriteNode(ulong txnId, LeafNode node, BrightstarProfiler profiler = null)
 {
     _pageStore.Write(txnId, node.PageId, node.GetData(), profiler: profiler);
     return(new KeyValuePair <byte[], ulong>(node.LeftmostKey, node.PageId));
 }
Ejemplo n.º 10
0
        public ILeafNode Split(ulong txnId, IPage rightNodePage, out byte[] splitKey)
        {
            EnsureWriteable(txnId);
            Next = rightNodePage.Id;
            splitKey = new byte[_config.KeySize];
            int numToMove = KeyCount - _config.LeafSplitIndex;
            Array.Copy(_page.Data, KeyOffset(_config.LeafSplitIndex), splitKey, 0, _config.KeySize);
#if DEBUG_BTREE
            _config.BTreeDebug("LeafNode.Split. SplitKey={0}. NumToMove={1}. Structure Before: {2}", splitKey.Dump(), numToMove, Dump());
            _config.BTreeDebug("LeafNode.Split@{0}. Keys before: {1}", PageId, DumpKeys());
#endif
            rightNodePage.SetData(_page.Data, KeyOffset(_config.LeafSplitIndex),
                                  KeyOffset(0), numToMove*_config.KeySize);
            if (_config.ValueSize > 0)
            {
                rightNodePage.SetData(_page.Data, ValueOffset(_config.LeafSplitIndex),
                                      ValueOffset(0), numToMove*_config.ValueSize);
            }
            var rightNodeKeyCount = numToMove;
            rightNodePage.SetData(BitConverter.GetBytes(rightNodeKeyCount), 0, 0, 4);
            var rightNode = new LeafNode(rightNodePage, rightNodeKeyCount, _config);
            KeyCount = _config.LeafSplitIndex;
#if DEBUG_BTREE
_config.BTreeDebug("LeafNode.Split. Structure After : {0}. Right Node After: {1}",
    Dump(), rightNode.Dump());
_config.BTreeDebug("LeafNode.Split@{0}. Keys after: {1}", PageId, DumpKeys());
#endif
            return rightNode;
        }
Ejemplo n.º 11
0
 /// <summary>
 /// Attempts to ensure that the minimum size for this node is achieved by transferring entries from the right-hand sibling
 /// </summary>
 /// <param name="rightNode">The right-hand sibling that will provide entries</param>
 /// <returns>True if the node achieves its minimum size by the redistribution process, false otherwise</returns>
 public bool RedistributeFromRight(LeafNode rightNode)
 {
     int copyCount = (_keyCount + rightNode._keyCount)/2 - _keyCount;
     if (copyCount > 0)
     {
         // Copy keys and data segments from right node
         Array.Copy(rightNode._keys, 0, _keys, _keyCount, copyCount);
         Array.Copy(rightNode._dataSegments, 0, _dataSegments, _keyCount, copyCount);
         // Shift up the remaining keys and data segments in the right node
         for (int i = 0; i < rightNode._keyCount - copyCount; i++)
         {
             rightNode._keys[i] = rightNode._keys[i + copyCount];
             rightNode._dataSegments[i] = rightNode._dataSegments[i + copyCount];
         }
         rightNode._keyCount -= copyCount;
         _keyCount += copyCount;
         return true;
     }
     return false;
 }
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="leftNode">The left-hand sibling that will provide entries</param>
 /// <returns>True if the node achieves its minimum size by the redistribution process, false otherwise</returns>
 public bool RedistributeFromLeft(LeafNode leftNode)
 {
     int copyCount = (_keyCount + leftNode.KeyCount)/2 - _keyCount;
     if (copyCount > 0)
     {
         // Move down my data segments to make room for the copied ones
         for (int i = _keyCount - 1; i >= 0; i--)
         {
             _keys[i + copyCount] = _keys[i];
             _dataSegments[i + copyCount] = _dataSegments[i];
         }
         // Copy keys and data segments from left node
         Array.Copy(leftNode._keys, leftNode._keyCount - copyCount, _keys, 0, copyCount);
         Array.Copy(leftNode._dataSegments, leftNode._keyCount - copyCount, _dataSegments, 0, copyCount);
         _keyCount += copyCount;
         leftNode._keyCount -= copyCount;
         return true;
     }
     return false;
 }
Ejemplo n.º 13
0
 /// <summary>
 /// Split this leaf node into two
 /// </summary>
 /// <param name="newNodeId">The ID of the page reserved to store the new node</param>
 /// <param name="splitKey">Receives the key that was used for the split</param>
 /// <returns>The new node created by the split</returns>
 /// <remarks>The split operation always creates a new node for the upper (right-hand) half of the keys and keeps the lower (left-hand) half in this leaf node.</remarks>
 public LeafNode Split(ulong newNodeId, out byte[] splitKey)
 {
     var rightNode = new LeafNode(newNodeId, PageId, _nextPointer, _config);
     _nextPointer = newNodeId;
     splitKey = new byte[_config.KeySize];
     Array.Copy(_keys[_config.LeafSplitIndex], splitKey, _config.KeySize);
     Array.Copy(_keys, _config.LeafSplitIndex, rightNode._keys, 0, _keyCount - _config.LeafSplitIndex);
     Array.Copy(_dataSegments, _config.LeafSplitIndex, rightNode._dataSegments, 0,
                _keyCount - _config.LeafSplitIndex);
     rightNode._keyCount = _keyCount - _config.LeafSplitIndex;
     _keyCount = _config.LeafSplitIndex;
     return rightNode;
 }
Ejemplo n.º 14
0
 private KeyValuePair<byte[], ulong> WriteNode(ulong txnId, LeafNode node, BrightstarProfiler profiler = null)
 {
     _pageStore.Write(txnId, node.PageId, node.GetData(), profiler: profiler);
     return new KeyValuePair<byte[], ulong>(node.LeftmostKey, node.PageId);
 }