Example #1
0
        private void AddToRecentlyFoundPages(List <long> c, TreePage p, bool leftmostPage, bool rightmostPage)
        {
            Slice firstKey;

            if (leftmostPage == true)
            {
                firstKey = Slices.BeforeAllKeys;
            }
            else
            {
                // We are going to store the slice, therefore we copy.
                firstKey = p.GetNodeKey(_llt, 0, ByteStringType.Immutable);
            }

            Slice lastKey;

            if (rightmostPage == true)
            {
                lastKey = Slices.AfterAllKeys;
            }
            else
            {
                // We are going to store the slice, therefore we copy.
                lastKey = p.GetNodeKey(_llt, p.NumberOfEntries - 1, ByteStringType.Immutable);
            }

            var foundPage = new RecentlyFoundTreePages.FoundTreePage(p.PageNumber, p, firstKey, lastKey, c.ToArray());

            _recentlyFoundPages.Add(foundPage);
        }
Example #2
0
        public long GetParentPageOf(TreePage page)
        {
            Func <TreeCursor> cursorConstructor;
            TreeNodeHeader *  node;
            var p = FindPageFor(page.IsLeaf ? page.GetNodeKey(_llt, 0) : page.GetNodeKey(_llt, 1), out node, out cursorConstructor);

            if (p == null || p.LastMatch != 0)
            {
                return(-1);
            }

            using (var cursor = cursorConstructor())
            {
                while (cursor.PageCount > 0)
                {
                    if (cursor.CurrentPage.PageNumber == page.PageNumber)
                    {
                        if (cursor.PageCount == 1)
                        {
                            return(-1);// root page
                        }
                        return(cursor.ParentPage.PageNumber);
                    }
                    cursor.Pop();
                }
            }

            return(-1);
        }
Example #3
0
        public long GetParentPageOf(TreePage page)
        {
            Debug.Assert(page.IsCompressed == false);

            TreePage p;
            Slice    key;

            using (page.IsLeaf ? page.GetNodeKey(_llt, 0, out key) : page.GetNodeKey(_llt, 1, out key))
            {
                TreeCursorConstructor cursorConstructor;
                TreeNodeHeader *      node;
                p = FindPageFor(key, node: out node, cursor: out cursorConstructor, allowCompressed: true);

                if (page.IsLeaf && p.LastMatch != 0)
                {
                    if (p.IsCompressed == false)
                    {
                        // if a found page is compressed then we could not find the exact match because
                        // the key we were looking for might belong to an compressed entry
                        // if the page isn't compressed then it's a corruption

                        VoronUnrecoverableErrorException.Raise(_tx.LowLevelTransaction.Environment,
                                                               $"Could not find a page containing {key} when looking for a parent of {page}. Page {p} was found, last match: {p.LastMatch}.");
                    }
#if DEBUG
                    using (var decompressed = DecompressPage(p, skipCache: true))
                    {
                        decompressed.Search(_llt, key);
                        Debug.Assert(decompressed.LastMatch == 0);
                    }
#endif
                }

                using (var cursor = cursorConstructor.Build(key))
                {
                    while (cursor.PageCount > 0)
                    {
                        if (cursor.CurrentPage.PageNumber == page.PageNumber)
                        {
                            if (cursor.PageCount == 1)
                            {
                                return(-1); // root page
                            }
                            return(cursor.ParentPage.PageNumber);
                        }
                        cursor.Pop();
                    }
                }
            }

            return(-1);
        }
Example #4
0
        public long GetParentPageOf(TreePage page)
        {
            Debug.Assert(page.IsCompressed == false);

            TreePage p;
            Slice    key;

            using (page.IsLeaf ? page.GetNodeKey(_llt, 0, out key) : page.GetNodeKey(_llt, 1, out key))
            {
                Func <Slice, TreeCursor> cursorConstructor;
                TreeNodeHeader *         node;
                p = FindPageFor(key, node: out node, cursor: out cursorConstructor, allowCompressed: true);

                if (p.LastMatch != 0)
                {
                    if (p.IsCompressed == false)
                    {
                        ThrowOnCompressedPage(p);
                    }
#if DEBUG
                    using (var decompressed = DecompressPage(p, skipCache: true))
                    {
                        decompressed.Search(_llt, key);
                        Debug.Assert(decompressed.LastMatch == 0);
                    }
#endif
                }

                using (var cursor = cursorConstructor(key))
                {
                    while (cursor.PageCount > 0)
                    {
                        if (cursor.CurrentPage.PageNumber == page.PageNumber)
                        {
                            if (cursor.PageCount == 1)
                            {
                                return(-1); // root page
                            }
                            return(cursor.ParentPage.PageNumber);
                        }
                        cursor.Pop();
                    }
                }
            }

            return(-1);
        }
Example #5
0
        private void AddToRecentlyFoundPages(TreeCursor c, TreePage p, bool leftmostPage, bool rightmostPage)
        {
            ByteStringContext.Scope firstScope, lastScope;
            Slice firstKey;

            if (leftmostPage)
            {
                firstScope = new ByteStringContext <ByteStringMemoryCache> .Scope();

                firstKey = Slices.BeforeAllKeys;
            }
            else
            {
                // We are going to store the slice, therefore we copy.
                firstScope = p.GetNodeKey(_llt, 0, ByteStringType.Immutable, out firstKey);
            }

            Slice lastKey;

            if (rightmostPage)
            {
                lastScope = new ByteStringContext <ByteStringMemoryCache> .Scope();

                lastKey = Slices.AfterAllKeys;
            }
            else
            {
                // We are going to store the slice, therefore we copy.
                lastScope = p.GetNodeKey(_llt, p.NumberOfEntries - 1, ByteStringType.Immutable, out lastKey);
            }

            var cursorPath = new long[c.Pages.Count];

            var cur = c.Pages.First;
            int pos = cursorPath.Length - 1;

            while (cur != null)
            {
                cursorPath[pos--] = cur.Value.PageNumber;
                cur = cur.Next;
            }

            var foundPage = new RecentlyFoundTreePages.FoundTreePage(p.PageNumber, p, firstKey, lastKey, cursorPath, firstScope, lastScope);

            _recentlyFoundPages.Add(foundPage);
        }
Example #6
0
        private void AddToRecentlyFoundPages(List <long> c, TreePage p, bool leftmostPage, bool rightmostPage)
        {
            Debug.Assert(p.IsCompressed == false);

            ByteStringContext.Scope firstScope, lastScope;
            Slice firstKey;

            if (leftmostPage)
            {
                firstScope = new ByteStringContext <ByteStringMemoryCache> .Scope();

                firstKey = Slices.BeforeAllKeys;
            }
            else
            {
                // We are going to store the slice, therefore we copy.
                firstScope = p.GetNodeKey(_llt, 0, ByteStringType.Immutable, out firstKey);
            }

            Slice lastKey;

            if (rightmostPage)
            {
                lastScope = new ByteStringContext <ByteStringMemoryCache> .Scope();

                lastKey = Slices.AfterAllKeys;
            }
            else
            {
                // We are going to store the slice, therefore we copy.
                lastScope = p.GetNodeKey(_llt, p.NumberOfEntries - 1, ByteStringType.Immutable, out lastKey);
            }

            var foundPage = new RecentlyFoundTreePages.FoundTreePage(p.PageNumber, p, firstKey, lastKey, c.ToArray(), firstScope, lastScope);

            _recentlyFoundPages.Add(foundPage);
        }
Example #7
0
        private byte *SplitPageInHalf(TreePage rightPage)
        {
            bool toRight;

            var currentIndex = _page.LastSearchPosition;
            var splitIndex   = _page.NumberOfEntries / 2;

            if (currentIndex <= splitIndex)
            {
                toRight = false;
            }
            else
            {
                toRight = true;

                var leftPageEntryCount  = splitIndex;
                var rightPageEntryCount = _page.NumberOfEntries - leftPageEntryCount + 1;

                if (rightPageEntryCount > leftPageEntryCount)
                {
                    splitIndex++;

                    Debug.Assert(splitIndex < _page.NumberOfEntries);
                }
            }

            if (_page.IsLeaf)
            {
                splitIndex = AdjustSplitPosition(currentIndex, splitIndex, ref toRight);
            }

            Slice currentKey;

            using (_page.GetNodeKey(_tx, splitIndex, out currentKey))
            {
                Slice seperatorKey;
                if (toRight && splitIndex == currentIndex)
                {
                    seperatorKey = SliceComparer.Compare(currentKey, _newKey) < 0 ? currentKey : _newKey;
                }
                else
                {
                    seperatorKey = currentKey;
                }

                var      addedAsImplicitRef = false;
                var      parentOfPage       = _cursor.CurrentPage;
                TreePage parentOfRight;

                DecompressedLeafPage rightDecompressed = null;

                if (_pageDecompressed != null)
                {
                    // splitting the decompressed page, let's allocate the page of the same size to ensure enough space
                    rightDecompressed = _tx.Environment.DecompressionBuffers.GetPage(_tx, _pageDecompressed.PageSize, DecompressionUsage.Write, rightPage);
                    rightPage         = rightDecompressed;
                }

                using (rightDecompressed)
                {
                    AddSeparatorToParentPage(rightPage.PageNumber, seperatorKey, out parentOfRight);

                    if (_page.IsBranch && toRight && SliceComparer.EqualsInline(seperatorKey, _newKey))
                    {
                        // _newKey needs to be inserted as first key (BeforeAllKeys) to the right page, so we need to add it before we move entries from the current page
                        AddNodeToPage(rightPage, 0, Slices.BeforeAllKeys);
                        addedAsImplicitRef = true;
                    }

                    // move the actual entries from page to right page
                    ushort nKeys = _page.NumberOfEntries;
                    for (int i = splitIndex; i < nKeys; i++)
                    {
                        TreeNodeHeader *node = _page.GetNode(i);
                        if (_page.IsBranch && rightPage.NumberOfEntries == 0)
                        {
                            rightPage.CopyNodeDataToEndOfPage(node, Slices.BeforeAllKeys);
                        }
                        else
                        {
                            Slice instance;
                            using (TreeNodeHeader.ToSlicePtr(_tx.Allocator, node, out instance))
                            {
                                rightPage.CopyNodeDataToEndOfPage(node, instance);
                            }
                        }
                    }

                    if (rightDecompressed != null)
                    {
                        rightDecompressed.CopyToOriginal(_tx, defragRequired: false, wasModified: true);
                        rightPage = rightDecompressed.Original;
                    }
                }

                _page.Truncate(_tx, splitIndex);

                RecompressPageIfNeeded(wasModified: true);

                byte *pos;

                if (addedAsImplicitRef == false)
                {
                    try
                    {
                        if (toRight && _cursor.CurrentPage.PageNumber != parentOfRight.PageNumber)
                        {
                            // modify the cursor if we are going to insert to the right page
                            _cursor.Pop();
                            _cursor.Push(parentOfRight);
                        }

                        // actually insert the new key
                        pos = InsertNewKey(toRight ? rightPage : _page);
                    }
                    catch (InvalidOperationException e)
                    {
                        if (
                            e.Message.StartsWith("The page is full and cannot add an entry", StringComparison.Ordinal) ==
                            false)
                        {
                            throw;
                        }

                        throw new InvalidOperationException(
                                  GatherDetailedDebugInfo(rightPage, currentKey, seperatorKey, currentIndex, splitIndex,
                                                          toRight), e);
                    }
                }
                else
                {
                    pos = null;
                    _cursor.Push(rightPage);
                }

                if (_page.IsBranch)
                // remove a branch that has only one entry, the page ref needs to be added to the parent of the current page
                {
                    Debug.Assert(_page.NumberOfEntries > 0);
                    Debug.Assert(rightPage.NumberOfEntries > 0);

                    if (_page.NumberOfEntries == 1)
                    {
                        RemoveBranchWithOneEntry(_page, parentOfPage);
                    }

                    if (rightPage.NumberOfEntries == 1)
                    {
                        RemoveBranchWithOneEntry(rightPage, parentOfRight);
                    }
                }

                return(pos);
            }
        }
Example #8
0
        public void MultiAdd(Slice key, Slice value)
        {
            if (!value.HasValue)
            {
                throw new ArgumentNullException(nameof(value));
            }

            int maxNodeSize = Llt.DataPager.NodeMaxSize;

            if (value.Size > maxNodeSize)
            {
                throw new ArgumentException("Cannot add a value to child tree that is over " + maxNodeSize + " bytes in size", nameof(value));
            }
            if (value.Size == 0)
            {
                throw new ArgumentException("Cannot add empty value to child tree");
            }

            State.IsModified = true;
            State.Flags     |= TreeFlags.MultiValueTrees;

            TreeNodeHeader *node;
            var             page = FindPageFor(key, out node);

            if (page == null || page.LastMatch != 0)
            {
                MultiAddOnNewValue(key, value, maxNodeSize);
                return;
            }

            page = ModifyPage(page);

            var   item = page.GetNode(page.LastSearchPosition);
            byte *_;

            // already was turned into a multi tree, not much to do here
            if (item->Flags == TreeNodeFlags.MultiValuePageRef)
            {
                var existingTree = OpenMultiValueTree(key, item);
                existingTree.DirectAdd(value, 0, out _).Dispose();
                return;
            }

            if (item->Flags == TreeNodeFlags.PageRef)
            {
                throw new InvalidOperationException("Multi trees don't use overflows");
            }

            var nestedPagePtr = DirectAccessFromHeader(item);

            var nestedPage = new TreePage(nestedPagePtr, (ushort)GetDataSize(item));

            var existingItem = nestedPage.Search(_llt, value);

            if (nestedPage.LastMatch != 0)
            {
                existingItem = null;// not an actual match, just greater than
            }
            if (existingItem != null)
            {
                // maybe same value added twice?
                Slice tmpKey;
                using (TreeNodeHeader.ToSlicePtr(_llt.Allocator, item, out tmpKey))
                {
                    if (SliceComparer.Equals(tmpKey, value))
                    {
                        return; // already there, turning into a no-op
                    }
                }

                nestedPage.RemoveNode(nestedPage.LastSearchPosition);
            }

            if (nestedPage.HasSpaceFor(_llt, value, 0))
            {
                // we are now working on top of the modified root page, we can just modify the memory directly
                nestedPage.AddDataNode(nestedPage.LastSearchPosition, value, 0);
                return;
            }

            if (page.HasSpaceFor(_llt, value, 0))
            {
                // page has space for an additional node in nested page ...

                var requiredSpace = nestedPage.PageSize +                  // existing page
                                    nestedPage.GetRequiredSpace(value, 0); // new node

                if (requiredSpace + Constants.Tree.NodeHeaderSize <= maxNodeSize)
                {
                    // ... and it won't require to create an overflow, so we can just expand the current value, no need to create a nested tree yet

                    EnsureNestedPagePointer(page, item, ref nestedPage, ref nestedPagePtr);

                    var newPageSize = (ushort)Math.Min(Bits.NextPowerOf2(requiredSpace), maxNodeSize - Constants.Tree.NodeHeaderSize);

                    ExpandMultiTreeNestedPageSize(key, value, nestedPagePtr, newPageSize, nestedPage.PageSize);

                    return;
                }
            }

            EnsureNestedPagePointer(page, item, ref nestedPage, ref nestedPagePtr);

            // we now have to convert this into a tree instance, instead of just a nested page
            var tree = Create(_llt, _tx, key, TreeFlags.MultiValue);

            for (int i = 0; i < nestedPage.NumberOfEntries; i++)
            {
                Slice existingValue;
                using (nestedPage.GetNodeKey(_llt, i, out existingValue))
                {
                    tree.DirectAdd(existingValue, 0, out _).Dispose();
                }
            }
            tree.DirectAdd(value, 0, out _).Dispose();
            _tx.AddMultiValueTree(this, key, tree);
            // we need to record that we switched to tree mode here, so the next call wouldn't also try to create the tree again
            DirectAdd(key, sizeof(TreeRootHeader), TreeNodeFlags.MultiValuePageRef, out _).Dispose();
        }
        private byte *SplitPageInHalf(TreePage rightPage)
        {
            bool toRight;

            var currentIndex = _page.LastSearchPosition;
            var splitIndex   = _page.NumberOfEntries / 2;

            if (currentIndex <= splitIndex)
            {
                toRight = false;
            }
            else
            {
                toRight = true;

                var leftPageEntryCount  = splitIndex;
                var rightPageEntryCount = _page.NumberOfEntries - leftPageEntryCount + 1;

                if (rightPageEntryCount > leftPageEntryCount)
                {
                    splitIndex++;

                    Debug.Assert(splitIndex < _page.NumberOfEntries);
                }
            }

            if (_page.IsLeaf)
            {
                splitIndex = AdjustSplitPosition(currentIndex, splitIndex, ref toRight);
            }

            var currentKey = _page.GetNodeKey(_tx, splitIndex);

            Slice seperatorKey;

            if (toRight && splitIndex == currentIndex)
            {
                seperatorKey = SliceComparer.Compare(currentKey, _newKey) < 0 ? currentKey : _newKey;
            }
            else
            {
                seperatorKey = currentKey;
            }

            TreePage parentOfRight;

            AddSeparatorToParentPage(rightPage.PageNumber, seperatorKey, out parentOfRight);

            var parentOfPage = _cursor.CurrentPage;

            bool addedAsImplicitRef = false;

            if (_page.IsBranch && toRight && SliceComparer.EqualsInline(seperatorKey, _newKey))
            {
                // _newKey needs to be inserted as first key (BeforeAllKeys) to the right page, so we need to add it before we move entries from the current page
                AddNodeToPage(rightPage, 0, Slices.BeforeAllKeys);
                addedAsImplicitRef = true;
            }

            // move the actual entries from page to right page
            var    instance = new Slice();
            ushort nKeys    = _page.NumberOfEntries;

            for (int i = splitIndex; i < nKeys; i++)
            {
                TreeNodeHeader *node = _page.GetNode(i);
                if (_page.IsBranch && rightPage.NumberOfEntries == 0)
                {
                    rightPage.CopyNodeDataToEndOfPage(node, Slices.BeforeAllKeys);
                }
                else
                {
                    instance = TreeNodeHeader.ToSlicePtr(_tx.Allocator, node);
                    rightPage.CopyNodeDataToEndOfPage(node, instance);
                }
            }

            _page.Truncate(_tx, splitIndex);

            byte *pos;

            if (addedAsImplicitRef == false)
            {
                try
                {
                    if (toRight && _cursor.CurrentPage.PageNumber != parentOfRight.PageNumber)
                    {
                        // modify the cursor if we are going to insert to the right page
                        _cursor.Pop();
                        _cursor.Push(parentOfRight);
                    }

                    // actually insert the new key
                    pos = toRight ? InsertNewKey(rightPage) : InsertNewKey(_page);
                }
                catch (InvalidOperationException e)
                {
                    if (e.Message.StartsWith("The page is full and cannot add an entry", StringComparison.Ordinal) == false)
                    {
                        throw;
                    }

                    throw new InvalidOperationException(GatherDetailedDebugInfo(rightPage, currentKey, seperatorKey, currentIndex, splitIndex, toRight), e);
                }
            }
            else
            {
                pos = null;
                _cursor.Push(rightPage);
            }

            if (_page.IsBranch) // remove a branch that has only one entry, the page ref needs to be added to the parent of the current page
            {
                Debug.Assert(_page.NumberOfEntries > 0);
                Debug.Assert(rightPage.NumberOfEntries > 0);

                if (_page.NumberOfEntries == 1)
                {
                    RemoveBranchWithOneEntry(_page, parentOfPage);
                }

                if (rightPage.NumberOfEntries == 1)
                {
                    RemoveBranchWithOneEntry(rightPage, parentOfRight);
                }
            }

            return(pos);
        }