Esempio n. 1
0
        public bool MovePrev()
        {
            if (_disposed)
            {
                throw new ObjectDisposedException("TreeIterator " + _tree.Name);
            }
            while (true)
            {
                _currentPage.LastSearchPosition--;
                if (_currentPage.LastSearchPosition >= 0)
                {
                    // run out of entries, need to select the next page...
                    while (_currentPage.IsBranch)
                    {
                        // In here we will also have the 'current' page (even if we are traversing a compressed node).
                        if (_prefetch)
                        {
                            MaybePrefetchPagesReferencedBy(_currentPage);
                        }

                        _cursor.Push(_currentPage);
                        var node = _currentPage.GetNode(_currentPage.LastSearchPosition);
                        _currentPage = _tree.GetReadOnlyTreePage(node->PageNumber);

                        if (_currentPage.IsCompressed)
                        {
                            DecompressedCurrentPage();
                        }

                        _currentPage.LastSearchPosition = _currentPage.NumberOfEntries - 1;
                    }

                    // We should be prefetching data pages down here.
                    if (_prefetch)
                    {
                        MaybePrefetchPagesReferencedBy(_currentPage);
                    }

                    var current = _currentPage.GetNode(_currentPage.LastSearchPosition);

                    if (DoRequireValidation && this.ValidateCurrentKey(_tx, current) == false)
                    {
                        return(false);
                    }

                    _prevKeyScope.Dispose();
                    _prevKeyScope = TreeNodeHeader.ToSlicePtr(_tx.Allocator, current, out _currentInternalKey);
                    _currentKey   = _currentInternalKey;

                    return(true);// there is another entry in this page
                }
                if (_cursor.PageCount == 0)
                {
                    break;
                }
                _currentPage = _cursor.Pop();
            }
            _currentPage = null;
            return(false);
        }
Esempio n. 2
0
        private TreePage SetupMoveOrMerge(TreePage page, TreePage parentPage)
        {
            TreePage sibling;

            if (parentPage.LastSearchPosition == 0) // we are the left most item
            {
                sibling = _tree.ModifyPage(parentPage.GetNode(1)->PageNumber);

                sibling.LastSearchPosition    = 0;
                page.LastSearchPosition       = page.NumberOfEntries;
                parentPage.LastSearchPosition = 1;
            }
            else // there is at least 1 page to our left
            {
                var beyondLast = parentPage.LastSearchPosition == parentPage.NumberOfEntries;
                if (beyondLast)
                {
                    parentPage.LastSearchPosition--;
                }
                parentPage.LastSearchPosition--;
                sibling = _tree.ModifyPage(parentPage.GetNode(parentPage.LastSearchPosition)->PageNumber);
                parentPage.LastSearchPosition++;
                if (beyondLast)
                {
                    parentPage.LastSearchPosition++;
                }
                sibling.LastSearchPosition = sibling.NumberOfEntries - 1;
                page.LastSearchPosition    = 0;
            }
            return(sibling);
        }
Esempio n. 3
0
        private byte *OptimizedOnlyMoveNewValueToTheRightPage(TreePage rightPage)
        {
            // when we get a split at the end of the page, we take that as a hint that the user is doing
            // sequential inserts, at that point, we are going to keep the current page as is and create a new
            // page, this will allow us to do minimal amount of work to get the best density

            TreePage branchOfSeparator;

            byte *pos;

            if (_page.IsBranch)
            {
                if (_page.NumberOfEntries > 2)
                {
                    // here we steal the last entry from the current page so we maintain the implicit null left entry

                    TreeNodeHeader *node = _page.GetNode(_page.NumberOfEntries - 1);
                    Debug.Assert(node->Flags == TreeNodeFlags.PageRef);
                    rightPage.AddPageRefNode(0, Slices.BeforeAllKeys, node->PageNumber);
                    pos = AddNodeToPage(rightPage, 1);

                    Slice separatorKey;
                    using (TreeNodeHeader.ToSlicePtr(_tx.Allocator, node, out separatorKey))
                    {
                        AddSeparatorToParentPage(rightPage.PageNumber, separatorKey, out branchOfSeparator);
                    }

                    _page.RemoveNode(_page.NumberOfEntries - 1);
                }
                else
                {
                    _tree.FreePage(rightPage); // return the unnecessary right page
                    pos = AddSeparatorToParentPage(_pageNumber, _newKey, out branchOfSeparator);

                    if (_cursor.CurrentPage.PageNumber != branchOfSeparator.PageNumber)
                    {
                        _cursor.Push(branchOfSeparator);
                    }

                    return(pos);
                }
            }
            else
            {
                AddSeparatorToParentPage(rightPage.PageNumber, _newKey, out branchOfSeparator);
                pos = AddNodeToPage(rightPage, 0);
            }
            _cursor.Push(rightPage);
            return(pos);
        }
Esempio n. 4
0
        private Slice GetActualKey(TreePage page, int pos, out TreeNodeHeader *node)
        {
            node = page.GetNode(pos);
            var key = TreeNodeHeader.ToSlicePtr(_tx.Allocator, node);

            while (key.Size == 0)
            {
                Debug.Assert(page.IsBranch);
                page = _tx.GetReadOnlyTreePage(node->PageNumber);
                node = page.GetNode(0);
                key  = TreeNodeHeader.ToSlicePtr(_tx.Allocator, node);
            }

            return(key);
        }
Esempio n. 5
0
        private void RebalanceRoot(TreePage page)
        {
            if (page.NumberOfEntries == 0)
            {
                return; // nothing to do
            }
            if (!page.IsBranch || page.NumberOfEntries > 1)
            {
                return; // cannot do anything here
            }
            // in this case, we have a root pointer with just one pointer, we can just swap it out

            var node = page.GetNode(0);

            Debug.Assert(node->Flags == (TreeNodeFlags.PageRef));

            var rootPage = _tree.ModifyPage(node->PageNumber);

            _tree.State.RootPageNumber = rootPage.PageNumber;
            _tree.State.Depth--;

            Debug.Assert(rootPage.Dirty);

            _cursor.Pop();
            _cursor.Push(rootPage);

            _tree.FreePage(page);
        }
        private string GatherDetailedDebugInfo(TreePage rightPage, Slice currentKey, Slice seperatorKey, int currentIndex, int splitIndex, bool toRight)
        {
            var debugInfo = new StringBuilder();

            debugInfo.AppendFormat("\r\n_tree.Name: {0}\r\n", _tree.Name);
            debugInfo.AppendFormat("_newKey: {0}, _len: {1}, needed space: {2}\r\n", _newKey, _len, _page.GetRequiredSpace(_newKey, _len));
            debugInfo.AppendFormat("key at LastSearchPosition: {0}, current key: {1}, seperatorKey: {2}\r\n", _page.GetNodeKey(_tx, _page.LastSearchPosition), currentKey, seperatorKey);
            debugInfo.AppendFormat("currentIndex: {0}\r\n", currentIndex);
            debugInfo.AppendFormat("splitIndex: {0}\r\n", splitIndex);
            debugInfo.AppendFormat("toRight: {0}\r\n", toRight);

            debugInfo.AppendFormat("_page info: flags - {0}, # of entries {1}, size left: {2}, calculated size left: {3}\r\n", _page.TreeFlags, _page.NumberOfEntries, _page.SizeLeft, _page.CalcSizeLeft());

            for (int i = 0; i < _page.NumberOfEntries; i++)
            {
                var node = _page.GetNode(i);
                var key  = TreeNodeHeader.ToSlicePtr(_tx.Allocator, node);
                debugInfo.AppendFormat("{0} - {2} {1}\r\n", key,
                                       node->DataSize, node->Flags == TreeNodeFlags.Data ? "Size" : "Page");
            }

            debugInfo.AppendFormat("rightPage info: flags - {0}, # of entries {1}, size left: {2}, calculated size left: {3}\r\n", rightPage.TreeFlags, rightPage.NumberOfEntries, rightPage.SizeLeft, rightPage.CalcSizeLeft());

            for (int i = 0; i < rightPage.NumberOfEntries; i++)
            {
                var node = rightPage.GetNode(i);
                var key  = TreeNodeHeader.ToSlicePtr(_tx.Allocator, node);
                debugInfo.AppendFormat("{0} - {2} {1}\r\n", key,
                                       node->DataSize, node->Flags == TreeNodeFlags.Data ? "Size" : "Page");
            }
            return(debugInfo.ToString());
        }
Esempio n. 7
0
        private ByteStringContext.ExternalScope GetActualKey(TreePage page, int pos, out TreeNodeHeader *node, out Slice key)
        {
            node = page.GetNode(pos);
            var scope = TreeNodeHeader.ToSlicePtr(_tx.Allocator, node, out key);

            while (key.Size == 0)
            {
                Debug.Assert(page.IsBranch);
                page = _tree.GetReadOnlyTreePage(node->PageNumber);
                node = page.GetNode(0);
                scope.Dispose();
                scope = TreeNodeHeader.ToSlicePtr(_tx.Allocator, node, out key);
            }

            return(scope);
        }
Esempio n. 8
0
        private void RemoveBranchWithOneEntry(TreePage page, TreePage parentPage)
        {
            Debug.Assert(page.NumberOfEntries == 1);

            var pageRefNumber = page.GetNode(0)->PageNumber;

            TreeNodeHeader *nodeHeader = null;

            for (int i = 0; i < parentPage.NumberOfEntries; i++)
            {
                nodeHeader = parentPage.GetNode(i);

                if (nodeHeader->PageNumber == page.PageNumber)
                {
                    break;
                }
            }

            Debug.Assert(nodeHeader->PageNumber == page.PageNumber);

            nodeHeader->PageNumber = pageRefNumber;

            if (_cursor.CurrentPage.PageNumber == page.PageNumber)
            {
                _cursor.Pop();
                _cursor.Push(_tree.GetReadOnlyTreePage(pageRefNumber));
            }

            _tree.FreePage(page);
        }
Esempio n. 9
0
        private bool TryMergePages(TreePage parentPage, TreePage left, TreePage right)
        {
            TemporaryPage tmp;

            using (_tx.Environment.GetTemporaryPage(_tx, out tmp))
            {
                var mergedPage = tmp.GetTempPage();
                Memory.Copy(mergedPage.Base, left.Base, left.PageSize);

                var previousSearchPosition = right.LastSearchPosition;

                for (int i = 0; i < right.NumberOfEntries; i++)
                {
                    right.LastSearchPosition = i;

                    var key  = GetActualKey(right, right.LastSearchPositionOrLastEntry);
                    var node = right.GetNode(i);

                    if (mergedPage.HasSpaceFor(_tx, TreeSizeOf.NodeEntryWithAnotherKey(node, key) + Constants.NodeOffsetSize) == false)
                    {
                        right.LastSearchPosition = previousSearchPosition; //previous position --> prevent mutation of parameter
                        return(false);
                    }

                    mergedPage.CopyNodeDataToEndOfPage(node, key);
                }

                Memory.Copy(left.Base, mergedPage.Base, left.PageSize);
            }

            parentPage.RemoveNode(parentPage.LastSearchPositionOrLastEntry); // unlink the right sibling
            _tree.FreePage(right);

            return(true);
        }
Esempio n. 10
0
        public bool MovePrev()
        {
            if (_disposed)
            {
                throw new ObjectDisposedException("TreeIterator " + _tree.Name);
            }
            while (true)
            {
                _currentPage.LastSearchPosition--;
                if (_currentPage.LastSearchPosition >= 0)
                {
                    // run out of entries, need to select the next page...
                    while (_currentPage.IsBranch)
                    {
                        _cursor.Push(_currentPage);
                        var node = _currentPage.GetNode(_currentPage.LastSearchPosition);
                        _currentPage = _tree.GetReadOnlyTreePage(node->PageNumber);
                        _currentPage.LastSearchPosition = _currentPage.NumberOfEntries - 1;

                        if (_prefetch && _currentPage.IsLeaf)
                        {
                            MaybePrefetchOverflowPages(_currentPage);
                        }
                    }
                    var current = _currentPage.GetNode(_currentPage.LastSearchPosition);

                    if (DoRequireValidation && this.ValidateCurrentKey(_tx, current) == false)
                    {
                        return(false);
                    }

                    _prevKeyScope.Dispose();
                    _prevKeyScope = TreeNodeHeader.ToSlicePtr(_tx.Allocator, current, out _currentInternalKey);
                    _currentKey   = _currentInternalKey;

                    return(true);// there is another entry in this page
                }
                if (_cursor.PageCount == 0)
                {
                    break;
                }
                _currentPage = _cursor.Pop();
            }
            _currentPage = null;
            return(false);
        }
Esempio n. 11
0
        private ActualKeyScope GetActualKey(TreePage page, int pos, out TreeNodeHeader *node, out Slice key)
        {
            DecompressedLeafPage decompressedLeafPage = null;

            node = page.GetNode(pos);
            var scope = TreeNodeHeader.ToSlicePtr(_tx.Allocator, node, out key);

            while (key.Size == 0)
            {
                Debug.Assert(page.IsBranch);
                page = _tree.GetReadOnlyTreePage(node->PageNumber);
                if (page.IsCompressed == false)
                {
                    node = page.GetNode(0);
                }
                else
                {
                    decompressedLeafPage?.Dispose();
                    decompressedLeafPage = _tree.DecompressPage(page, skipCache: true);

                    if (decompressedLeafPage.NumberOfEntries > 0)
                    {
                        node = decompressedLeafPage.GetNode(0);
                    }
                    else
                    {
                        // we have empty page after decompression (each compressed entry has a corresponding CompressionTombstone)
                        // we can safely use the node key of first tombstone (they have proper order)

                        node = page.GetNode(0);
                    }
                }

                scope.Dispose();
                scope = TreeNodeHeader.ToSlicePtr(_tx.Allocator, node, out key);
            }

            return(new ActualKeyScope
            {
                DecompressedLeafPage = decompressedLeafPage,
                ExternalScope = scope
            });
        }
Esempio n. 12
0
 private static void EnsureValidLastSearchPosition(TreePage page, long referencedPageNumber, int originalLastSearchPosition)
 {
     if (page.NumberOfEntries <= originalLastSearchPosition || page.GetNode(originalLastSearchPosition)->PageNumber != referencedPageNumber)
     {
         page.LastSearchPosition = page.NodePositionReferencing(referencedPageNumber);
     }
     else
     {
         page.LastSearchPosition = originalLastSearchPosition;
     }
 }
Esempio n. 13
0
        private void RemoveLeafNode(TreePage page)
        {
            var node = page.GetNode(page.LastSearchPosition);

            if (node->Flags == (TreeNodeFlags.PageRef)) // this is an overflow pointer
            {
                var overflowPage = GetReadOnlyTreePage(node->PageNumber);
                FreePage(overflowPage);
            }

            page.RemoveNode(page.LastSearchPosition);
        }
Esempio n. 14
0
        private void EnsureNestedPagePointer(TreePage page, TreeNodeHeader *currentItem, ref TreePage nestedPage, ref byte *nestedPagePtr)
        {
            var movedItem = page.GetNode(page.LastSearchPosition);

            if (movedItem == currentItem)
            {
                return;
            }

            // HasSpaceFor could called Defrag internally and read item has moved
            // need to ensure the nested page has a valid pointer

            nestedPagePtr = DirectAccessFromHeader(movedItem);
            nestedPage    = new TreePage(nestedPagePtr, (ushort)GetDataSize(movedItem));
        }
Esempio n. 15
0
        public bool MoveNext()
        {
            if (_disposed)
            {
                throw new ObjectDisposedException("TreeIterator " + _tree.Name);
            }

            while (_currentPage != null)
            {
                _currentPage.LastSearchPosition++;
                if (_currentPage.LastSearchPosition < _currentPage.NumberOfEntries)
                {
                    // run out of entries, need to select the next page...
                    while (_currentPage.IsBranch)
                    {
                        _cursor.Push(_currentPage);
                        var node = _currentPage.GetNode(_currentPage.LastSearchPosition);
                        _currentPage = _tx.GetReadOnlyTreePage(node->PageNumber);

                        _currentPage.LastSearchPosition = 0;
                    }
                    var current = _currentPage.GetNode(_currentPage.LastSearchPosition);
                    if (DoRequireValidation && this.ValidateCurrentKey(_tx, current) == false)
                    {
                        return(false);
                    }

                    _currentInternalKey = TreeNodeHeader.ToSlicePtr(_tx.Allocator, current, ByteStringType.Mutable);
                    _currentKey         = _currentInternalKey;
                    return(true);// there is another entry in this page
                }
                if (_cursor.PageCount == 0)
                {
                    break;
                }
                _currentPage = _cursor.Pop();
            }
            _currentPage = null;

            return(false);
        }
Esempio n. 16
0
        private void ExpandMultiTreeNestedPageSize(Slice key, Slice value, byte *nestedPagePtr, ushort newSize, int currentSize)
        {
            Debug.Assert(newSize > currentSize);

            TemporaryPage tmp;

            using (_llt.Environment.GetTemporaryPage(_llt, out tmp))
            {
                var tempPagePointer = tmp.TempPagePointer;
                Memory.Copy(tempPagePointer, nestedPagePtr, currentSize);
                Delete(key); // release our current page
                TreePage nestedPage = new TreePage(tempPagePointer, (ushort)currentSize);

                byte *ptr;
                using (DirectAdd(key, newSize, out ptr))
                {
                    var newNestedPage = new TreePage(ptr, newSize)
                    {
                        Lower      = (ushort)Constants.Tree.PageHeaderSize,
                        Upper      = newSize,
                        TreeFlags  = TreePageFlags.Leaf,
                        PageNumber = -1L, // mark as invalid page number
                        Flags      = 0
                    };

                    ByteStringContext allocator = _llt.Allocator;
                    for (int i = 0; i < nestedPage.NumberOfEntries; i++)
                    {
                        var nodeHeader = nestedPage.GetNode(i);

                        Slice nodeKey;
                        using (TreeNodeHeader.ToSlicePtr(allocator, nodeHeader, out nodeKey))
                            newNestedPage.AddDataNode(i, nodeKey, 0);
                    }

                    newNestedPage.Search(_llt, value);
                    newNestedPage.AddDataNode(newNestedPage.LastSearchPosition, value, 0);
                }
            }
        }
Esempio n. 17
0
        private void RemoveBranchWithOneEntry(TreePage page, TreePage parentPage)
        {
            var pageRefNumber = page.GetNode(0)->PageNumber;

            TreeNodeHeader *nodeHeader = null;

            for (int i = 0; i < parentPage.NumberOfEntries; i++)
            {
                nodeHeader = parentPage.GetNode(i);

                if (nodeHeader->PageNumber == page.PageNumber)
                {
                    break;
                }
            }

            Debug.Assert(nodeHeader->PageNumber == page.PageNumber);

            nodeHeader->PageNumber = pageRefNumber;

            _tree.FreePage(page);
        }
Esempio n. 18
0
        private void ExpandMultiTreeNestedPageSize(Slice key, Slice value, byte *nestedPagePtr, ushort newSize, int currentSize)
        {
            Debug.Assert(newSize > currentSize);

            TemporaryPage tmp;

            using (_llt.Environment.GetTemporaryPage(_llt, out tmp))
            {
                var tempPagePointer = tmp.TempPagePointer;
                Memory.Copy(tempPagePointer, nestedPagePtr, currentSize);
                Delete(key); // release our current page
                TreePage nestedPage = new TreePage(tempPagePointer, "multi tree", (ushort)currentSize);

                var ptr = DirectAdd(key, newSize);

                var newNestedPage = new TreePage(ptr, "multi tree", newSize)
                {
                    Lower      = (ushort)Constants.TreePageHeaderSize,
                    Upper      = newSize,
                    TreeFlags  = TreePageFlags.Leaf,
                    PageNumber = -1L // mark as invalid page number
                };

                ByteStringContext allocator = _llt.Allocator;
                for (int i = 0; i < nestedPage.NumberOfEntries; i++)
                {
                    var nodeHeader = nestedPage.GetNode(i);

                    Slice nodeKey = TreeNodeHeader.ToSlicePtr(allocator, nodeHeader);

                    newNestedPage.AddDataNode(i, nodeKey, 0, (ushort)(nodeHeader->Version - 1)); // we dec by one because AdddataNode will inc by one, and we don't want to change those values

                    nodeKey.Release(allocator);
                }

                newNestedPage.Search(_llt, value);
                newNestedPage.AddDataNode(newNestedPage.LastSearchPosition, value, 0, 0);
            }
        }
Esempio n. 19
0
        private bool TrySetPosition()
        {
            if (_disposed)
            {
                throw new ObjectDisposedException("PageIterator");
            }
            if (_page.LastSearchPosition < 0 || _page.LastSearchPosition >= _page.NumberOfEntries)
            {
                return(false);
            }

            var current = _page.GetNode(_page.LastSearchPosition);

            if (DoRequireValidation && this.ValidateCurrentKey(_tx, current) == false)
            {
                return(false);
            }

            _currentInternalKey = TreeNodeHeader.ToSlicePtr(_tx.Allocator, current);
            _currentKey         = _currentInternalKey;
            return(true);
        }
Esempio n. 20
0
        private void MoveBranchNode(TreePage parentPage, TreePage from, TreePage to)
        {
            Debug.Assert(from.IsBranch);

            Slice originalFromKey;

            using (GetActualKey(from, from.LastSearchPositionOrLastEntry, out originalFromKey))
            {
                to.EnsureHasSpaceFor(_tx, originalFromKey, -1);

                var  fromNode = from.GetNode(from.LastSearchPosition);
                long pageNum  = fromNode->PageNumber;

                if (to.LastSearchPosition == 0)
                {
                    // cannot add to left implicit side, adjust by moving the left node
                    // to the right by one, then adding the new one as the left

                    TreeNodeHeader *actualKeyNode;
                    Slice           implicitLeftKey;
                    using (GetActualKey(to, 0, out actualKeyNode, out implicitLeftKey))
                    {
                        var implicitLeftNode = to.GetNode(0);
                        var leftPageNumber   = implicitLeftNode->PageNumber;

                        Slice implicitLeftKeyToInsert;
                        ByteStringContext.ExternalScope?externalScope;

                        if (implicitLeftNode == actualKeyNode)
                        {
                            externalScope = TreeNodeHeader.ToSlicePtr(_tx.Allocator, actualKeyNode, out implicitLeftKeyToInsert);
                        }
                        else
                        {
                            implicitLeftKeyToInsert = implicitLeftKey;
                            externalScope           = null;
                        }

                        to.EnsureHasSpaceFor(_tx, implicitLeftKeyToInsert, -1);
                        to.AddPageRefNode(1, implicitLeftKeyToInsert, leftPageNumber);
                        externalScope?.Dispose();

                        to.ChangeImplicitRefPageNode(pageNum); // setup the new implicit node
                    }
                }
                else
                {
                    to.AddPageRefNode(to.LastSearchPosition, originalFromKey, pageNum);
                }
            }

            if (from.LastSearchPositionOrLastEntry == 0)
            {
                var rightPageNumber = from.GetNode(1)->PageNumber;
                from.RemoveNode(0);                              // remove the original implicit node
                from.ChangeImplicitRefPageNode(rightPageNumber); // setup the new implicit node
                Debug.Assert(from.NumberOfEntries >= 2);
            }
            else
            {
                from.RemoveNode(from.LastSearchPositionOrLastEntry);
            }

            var pos = parentPage.LastSearchPositionOrLastEntry;

            parentPage.RemoveNode(pos);
            Slice newSeparatorKey;
            var   scope = GetActualKey(to, 0, out newSeparatorKey); // get the next smallest key it has now

            try
            {
                var pageNumber = to.PageNumber;
                if (parentPage.GetNode(0)->PageNumber == to.PageNumber)
                {
                    pageNumber = from.PageNumber;
                    scope.Dispose();
                    scope = GetActualKey(from, 0, out newSeparatorKey);
                }

                AddSeparatorToParentPage(to, parentPage, pageNumber, newSeparatorKey, pos);
            }
            finally
            {
                scope.Dispose();
            }
        }
Esempio n. 21
0
        private ActualKeyScope GetActualKey(TreePage page, int pos, out TreeNodeHeader *node, out Slice key)
        {
            DecompressedLeafPage decompressedLeafPage = null;

            node = page.GetNode(pos);
            var scope = TreeNodeHeader.ToSlicePtr(_tx.Allocator, node, out key);

            while (key.Size == 0)
            {
                Debug.Assert(page.IsBranch);
                page = _tree.GetReadOnlyTreePage(node->PageNumber);
                if (page.IsCompressed == false)
                {
                    node = page.GetNode(0);
                }
                else
                {
                    decompressedLeafPage?.Dispose();
                    decompressedLeafPage = _tree.DecompressPage(page, DecompressionUsage.Read, skipCache: true);

                    if (decompressedLeafPage.NumberOfEntries > 0)
                    {
                        if (page.NumberOfEntries == 0)
                        {
                            node = decompressedLeafPage.GetNode(0);
                        }
                        else
                        {
                            // we want to find the smallest key in compressed page
                            // it can be inside compressed part or not compressed one
                            // in particular, it can be the key of compression tombstone node that we don't see after decompression
                            // so we need to take first keys from decompressed and compressed page and compare them

                            var decompressedNode = decompressedLeafPage.GetNode(0);
                            var compressedNode   = page.GetNode(0);

                            using (TreeNodeHeader.ToSlicePtr(_tx.Allocator, decompressedNode, out var firstDecompressedKey))
                                using (TreeNodeHeader.ToSlicePtr(_tx.Allocator, compressedNode, out var firstCompressedKey))
                                {
                                    node = SliceComparer.CompareInline(firstDecompressedKey, firstCompressedKey) > 0 ? compressedNode : decompressedNode;
                                }
                        }
                    }
                    else
                    {
                        // we have empty page after decompression (each compressed entry has a corresponding CompressionTombstone)
                        // we can safely use the node key of first tombstone (they have proper order)

                        node = page.GetNode(0);
                    }
                }

                scope.Dispose();
                scope = TreeNodeHeader.ToSlicePtr(_tx.Allocator, node, out key);
            }

            return(new ActualKeyScope
            {
                DecompressedLeafPage = decompressedLeafPage,
                ExternalScope = scope
            });
        }
Esempio n. 22
0
        private void MoveLeafNode(TreePage parentPage, TreePage from, TreePage to)
        {
            Debug.Assert(from.IsBranch == false);
            var originalFromKeyStart = GetActualKey(from, from.LastSearchPositionOrLastEntry);

            var   fromNode = from.GetNode(from.LastSearchPosition);
            byte *val      = @from.Base + @from.KeysOffsets[@from.LastSearchPosition] + Constants.NodeHeaderSize + originalFromKeyStart.Size;

            var nodeVersion = fromNode->Version; // every time new node is allocated the version is increased, but in this case we do not want to increase it

            if (nodeVersion > 0)
            {
                nodeVersion -= 1;
            }

            byte *dataPos;
            var   fromDataSize = fromNode->DataSize;

            switch (fromNode->Flags)
            {
            case TreeNodeFlags.PageRef:
                to.EnsureHasSpaceFor(_tx, originalFromKeyStart, -1);
                dataPos = to.AddPageRefNode(to.LastSearchPosition, originalFromKeyStart, fromNode->PageNumber);
                break;

            case TreeNodeFlags.Data:
                to.EnsureHasSpaceFor(_tx, originalFromKeyStart, fromDataSize);
                dataPos = to.AddDataNode(to.LastSearchPosition, originalFromKeyStart, fromDataSize, nodeVersion);
                break;

            case TreeNodeFlags.MultiValuePageRef:
                to.EnsureHasSpaceFor(_tx, originalFromKeyStart, fromDataSize);
                dataPos = to.AddMultiValueNode(to.LastSearchPosition, originalFromKeyStart, fromDataSize, nodeVersion);
                break;

            default:
                throw new NotSupportedException("Invalid node type to move: " + fromNode->Flags);
            }

            if (dataPos != null && fromDataSize > 0)
            {
                Memory.Copy(dataPos, val, fromDataSize);
            }

            from.RemoveNode(from.LastSearchPositionOrLastEntry);

            var pos = parentPage.LastSearchPositionOrLastEntry;

            parentPage.RemoveNode(pos);

            var newSeparatorKey = GetActualKey(to, 0); // get the next smallest key it has now
            var pageNumber      = to.PageNumber;

            if (parentPage.GetNode(0)->PageNumber == to.PageNumber)
            {
                pageNumber      = from.PageNumber;
                newSeparatorKey = GetActualKey(from, 0);
            }

            AddSeparatorToParentPage(to, parentPage, pageNumber, newSeparatorKey, pos);
        }
Esempio n. 23
0
        public byte *Execute()
        {
            using (DisableFreeSpaceUsageIfSplittingRootTree())
            {
                TreePage rightPage = _tree.NewPage(_page.TreeFlags, 1);

                if (_cursor.PageCount == 0) // we need to do a root split
                {
                    TreePage newRootPage = _tree.NewPage(TreePageFlags.Branch, 1);
                    _cursor.Push(newRootPage);
                    _tree.State.RootPageNumber = newRootPage.PageNumber;
                    _tree.State.Depth++;

                    // now add implicit left page
                    newRootPage.AddPageRefNode(0, Slices.BeforeAllKeys, _page.PageNumber);
                    _parentPage = newRootPage;
                    _parentPage.LastSearchPosition++;
                }
                else
                {
                    // we already popped the page, so the current one on the stack is the parent of the page

                    _parentPage = _tree.ModifyPage(_cursor.CurrentPage);

                    _cursor.Update(_cursor.Pages.First, _parentPage);
                }

                if (_page.IsLeaf)
                {
                    _tree.ClearPagesCache();
                }

                if (_page.IsCompressed)
                {
                    _pageDecompressed = _tree.DecompressPage(_page);
                    _pageDecompressed.Search(_tx, _newKey);
                    _page = _pageDecompressed;
                }

                using (_pageDecompressed)
                {
                    if (_page.LastSearchPosition >= _page.NumberOfEntries)
                    {
                        // when we get a split at the end of the page, we take that as a hint that the user is doing
                        // sequential inserts, at that point, we are going to keep the current page as is and create a new
                        // page, this will allow us to do minimal amount of work to get the best density

                        TreePage branchOfSeparator;

                        byte *pos;
                        if (_page.IsBranch)
                        {
                            if (_page.NumberOfEntries > 2)
                            {
                                // here we steal the last entry from the current page so we maintain the implicit null left entry

                                TreeNodeHeader *node = _page.GetNode(_page.NumberOfEntries - 1);
                                Debug.Assert(node->Flags == TreeNodeFlags.PageRef);
                                rightPage.AddPageRefNode(0, Slices.BeforeAllKeys, node->PageNumber);
                                pos = AddNodeToPage(rightPage, 1);

                                Slice separatorKey;
                                using (TreeNodeHeader.ToSlicePtr(_tx.Allocator, node, out separatorKey))
                                {
                                    AddSeparatorToParentPage(rightPage.PageNumber, separatorKey, out branchOfSeparator);
                                }

                                _page.RemoveNode(_page.NumberOfEntries - 1);
                            }
                            else
                            {
                                _tree.FreePage(rightPage); // return the unnecessary right page
                                pos = AddSeparatorToParentPage(_pageNumber, _newKey, out branchOfSeparator);

                                if (_cursor.CurrentPage.PageNumber != branchOfSeparator.PageNumber)
                                {
                                    _cursor.Push(branchOfSeparator);
                                }

                                return(pos);
                            }
                        }
                        else
                        {
                            AddSeparatorToParentPage(rightPage.PageNumber, _newKey, out branchOfSeparator);
                            pos = AddNodeToPage(rightPage, 0);
                        }
                        _cursor.Push(rightPage);
                        return(pos);
                    }

                    return(SplitPageInHalf(rightPage));
                }
            }
        }
Esempio n. 24
0
        private void HandleUncompressedNodes(DecompressedLeafPage decompressedPage, TreePage p, DecompressionUsage usage)
        {
            int numberOfEntries = p.NumberOfEntries;

            for (var i = 0; i < numberOfEntries; i++)
            {
                var uncompressedNode = p.GetNode(i);

                Slice nodeKey;
                using (TreeNodeHeader.ToSlicePtr(_tx.Allocator, uncompressedNode, out nodeKey))
                {
                    if (uncompressedNode->Flags == TreeNodeFlags.CompressionTombstone)
                    {
                        HandleTombstone(decompressedPage, nodeKey, usage);
                        continue;
                    }

                    if (decompressedPage.HasSpaceFor(_llt, TreeSizeOf.NodeEntry(uncompressedNode)) == false)
                    {
                        throw new InvalidOperationException("Could not add uncompressed node to decompressed page");
                    }

                    int index;

                    if (decompressedPage.NumberOfEntries > 0)
                    {
                        Slice lastKey;
                        using (decompressedPage.GetNodeKey(_llt, decompressedPage.NumberOfEntries - 1, out lastKey))
                        {
                            // optimization: it's very likely that uncompressed nodes have greater keys than compressed ones
                            // when we insert sequential keys

                            var cmp = SliceComparer.CompareInline(nodeKey, lastKey);

                            if (cmp > 0)
                            {
                                index = decompressedPage.NumberOfEntries;
                            }
                            else
                            {
                                if (cmp == 0)
                                {
                                    // update of the last entry, just decrement NumberOfEntries in the page and
                                    // put it at the last position

                                    index = decompressedPage.NumberOfEntries - 1;
                                    decompressedPage.Lower -= Constants.Tree.NodeOffsetSize;
                                }
                                else
                                {
                                    index = decompressedPage.NodePositionFor(_llt, nodeKey);

                                    if (decompressedPage.LastMatch == 0) // update
                                    {
                                        decompressedPage.RemoveNode(index);

                                        if (usage == DecompressionUsage.Write)
                                        {
                                            State.NumberOfEntries--;
                                        }
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        // all uncompressed nodes were compresion tombstones which deleted all entries from the decompressed page
                        index = 0;
                    }

                    switch (uncompressedNode->Flags)
                    {
                    case TreeNodeFlags.PageRef:
                        decompressedPage.AddPageRefNode(index, nodeKey, uncompressedNode->PageNumber);
                        break;

                    case TreeNodeFlags.Data:
                        var pos       = decompressedPage.AddDataNode(index, nodeKey, uncompressedNode->DataSize);
                        var nodeValue = TreeNodeHeader.Reader(_llt, uncompressedNode);
                        Memory.Copy(pos, nodeValue.Base, nodeValue.Length);
                        break;

                    case TreeNodeFlags.MultiValuePageRef:
                        throw new NotSupportedException("Multi trees do not support compression");

                    default:
                        throw new NotSupportedException("Invalid node type to copye: " + uncompressedNode->Flags);
                    }
                }
            }
        }
Esempio n. 25
0
        private void MoveLeafNode(TreePage parentPage, TreePage from, TreePage to)
        {
            Debug.Assert(from.IsBranch == false);
            Slice originalFromKeyStart;

            using (GetActualKey(from, from.LastSearchPositionOrLastEntry, out originalFromKeyStart))
            {
                var   fromNode = from.GetNode(from.LastSearchPosition);
                byte *val      = @from.Base + @from.KeysOffsets[@from.LastSearchPosition] + Constants.Tree.NodeHeaderSize +
                                 originalFromKeyStart.Size;

                byte *dataPos;
                var   fromDataSize = fromNode->DataSize;
                switch (fromNode->Flags)
                {
                case TreeNodeFlags.PageRef:
                    to.EnsureHasSpaceFor(_tx, originalFromKeyStart, -1);
                    dataPos = to.AddPageRefNode(to.LastSearchPosition, originalFromKeyStart, fromNode->PageNumber);
                    break;

                case TreeNodeFlags.Data:
                    to.EnsureHasSpaceFor(_tx, originalFromKeyStart, fromDataSize);
                    dataPos = to.AddDataNode(to.LastSearchPosition, originalFromKeyStart, fromDataSize);
                    break;

                case TreeNodeFlags.MultiValuePageRef:
                    to.EnsureHasSpaceFor(_tx, originalFromKeyStart, fromDataSize);
                    dataPos = to.AddMultiValueNode(to.LastSearchPosition, originalFromKeyStart, fromDataSize);
                    break;

                default:
                    throw new NotSupportedException("Invalid node type to move: " + fromNode->Flags);
                }

                if (dataPos != null && fromDataSize > 0)
                {
                    Memory.Copy(dataPos, val, fromDataSize);
                }

                from.RemoveNode(from.LastSearchPositionOrLastEntry);

                var pos = parentPage.LastSearchPositionOrLastEntry;
                parentPage.RemoveNode(pos);

                Slice newSeparatorKey;
                var   scope = GetActualKey(to, 0, out newSeparatorKey); // get the next smallest key it has now
                try
                {
                    var pageNumber = to.PageNumber;
                    if (parentPage.GetNode(0)->PageNumber == to.PageNumber)
                    {
                        pageNumber = from.PageNumber;
                        scope.Dispose();
                        scope = GetActualKey(from, 0, out newSeparatorKey);
                    }

                    AddSeparatorToParentPage(to, parentPage, pageNumber, newSeparatorKey, pos);
                }
                finally
                {
                    scope.Dispose();
                }
            }
        }