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)); }
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)); }
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); }
/// <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); }
/// <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); }
/// <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); }
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; } } }
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())); } }
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)); }
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; }
/// <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; }
/// <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; }
/// <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; }
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); }