Пример #1
0
        private bool TryUseRecentTransactionPage(Slice key, out TreePage page, out TreeNodeHeader *node)
        {
            node = null;
            page = null;

            var foundPage = _recentlyFoundPages?.Find(key);

            if (foundPage == null)
            {
                return(false);
            }

            if (foundPage.Page != null)
            {
                // we can't share the same instance, Page instance may be modified by
                // concurrently run iterators
                page = new TreePage(foundPage.Page.Base, foundPage.Page.PageSize);
            }
            else
            {
                page = GetReadOnlyTreePage(foundPage.Number);
            }

            if (page.IsLeaf == false)
            {
                VoronUnrecoverableErrorException.Raise(_llt.Environment, "Index points to a non leaf page");
            }

            node = page.Search(_llt, key); // will set the LastSearchPosition

            return(true);
        }
Пример #2
0
        /// <summary>
        /// Internal method that is used when splitting pages
        /// No need to do any work here, we are always adding at the end
        /// </summary>
        internal void CopyNodeDataToEndOfPage(TreeNodeHeader *other, Slice key)
        {
            var index = NumberOfEntries;

            Debug.Assert(HasSpaceFor(TreeSizeOf.NodeEntryWithAnotherKey(other, key) + Constants.Tree.NodeOffsetSize));

            var nodeSize = TreeSizeOf.NodeEntryWithAnotherKey(other, key);

            Debug.Assert(IsBranch == false || index != 0 || key.Size == 0);// branch page's first item must be the implicit ref

            var newNode = AllocateNewNode(index, nodeSize);

            Debug.Assert(key.Size <= ushort.MaxValue);
            newNode->KeySize = (ushort)key.Size;
            newNode->Flags   = other->Flags;

            if (key.Options == SliceOptions.Key && key.Size > 0)
            {
                key.CopyTo((byte *)newNode + Constants.Tree.NodeHeaderSize);
            }

            if (IsBranch || other->Flags == (TreeNodeFlags.PageRef))
            {
                newNode->PageNumber = other->PageNumber;
                newNode->Flags      = TreeNodeFlags.PageRef;
                return;
            }
            newNode->DataSize = other->DataSize;
            Memory.Copy((byte *)newNode + Constants.Tree.NodeHeaderSize + key.Size,
                        (byte *)other + Constants.Tree.NodeHeaderSize + other->KeySize,
                        other->DataSize);
        }
Пример #3
0
        private bool TryOverwriteOverflowPages(TreeNodeHeader *updatedNode, int len, out byte *pos)
        {
            if (updatedNode->Flags == TreeNodeFlags.PageRef)
            {
                var readOnlyOverflowPage = GetReadOnlyTreePage(updatedNode->PageNumber);

                if (len <= readOnlyOverflowPage.OverflowSize)
                {
                    var availableOverflows = _llt.DataPager.GetNumberOfOverflowPages(readOnlyOverflowPage.OverflowSize);

                    var requestedOverflows = _llt.DataPager.GetNumberOfOverflowPages(len);

                    var overflowsToFree = availableOverflows - requestedOverflows;

                    for (int i = 0; i < overflowsToFree; i++)
                    {
                        _llt.FreePage(readOnlyOverflowPage.PageNumber + requestedOverflows + i);
                    }

                    State.RecordFreedPage(readOnlyOverflowPage, overflowsToFree);

                    var writtableOverflowPage = AllocateNewPage(_llt, TreePageFlags.Value, requestedOverflows, updatedNode->PageNumber);

                    writtableOverflowPage.Flags        = PageFlags.Overflow | PageFlags.VariableSizeTreePage;
                    writtableOverflowPage.OverflowSize = len;
                    pos = writtableOverflowPage.Base + Constants.Tree.PageHeaderSize;

                    PageModified?.Invoke(writtableOverflowPage.PageNumber);

                    return(true);
                }
            }
            pos = null;
            return(false);
        }
        private TreePage GetNestedMultiValuePage(byte *nestedPagePtr, TreeNodeHeader *currentNode)
        {
            var nestedPage = new TreePage(nestedPagePtr, "multi tree", (ushort)TreeNodeHeader.GetDataSize(_tx, currentNode));

            Debug.Assert(nestedPage.PageNumber == -1); // nested page marker
            return(nestedPage);
        }
Пример #5
0
        private bool TryOverwriteDataOrMultiValuePageRefNode(TreeNodeHeader *updatedNode, int len,
                                                             TreeNodeFlags requestedNodeType, out byte *pos)
        {
            switch (requestedNodeType)
            {
            case TreeNodeFlags.Data:
            case TreeNodeFlags.MultiValuePageRef:
            {
                if (updatedNode->DataSize == len &&
                    (updatedNode->Flags == TreeNodeFlags.Data || updatedNode->Flags == TreeNodeFlags.MultiValuePageRef))
                {
                    updatedNode->Flags = requestedNodeType;

                    pos = (byte *)updatedNode + Constants.Tree.NodeHeaderSize + updatedNode->KeySize;
                    return(true);
                }
                break;
            }

            case TreeNodeFlags.PageRef:
                throw new InvalidOperationException("We never add PageRef explicitly");

            default:
                throw new ArgumentOutOfRangeException();
            }
            pos = null;
            return(false);
        }
Пример #6
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);
        }
Пример #7
0
                public TreeNodeHeaderSafe(Tree tree, TreeNodeHeader *nodeHeader)
                {
                    _tree       = tree ?? throw new ArgumentNullException(nameof(tree));
                    _nodeHeader = nodeHeader;

                    using (TreeNodeHeader.ToSlicePtr(tree.Llt.Allocator, nodeHeader, out Slice keySlice))
                        Key = keySlice.ToString();
                }
Пример #8
0
 public static int GetDataSize(LowLevelTransaction tx, TreeNodeHeader *node)
 {
     if (node->Flags == (TreeNodeFlags.PageRef))
     {
         var overFlowPage = tx.GetPage(node->PageNumber);
         return(overFlowPage.OverflowSize);
     }
     return(node->DataSize);
 }
Пример #9
0
 public static void CopyTo(LowLevelTransaction tx, TreeNodeHeader *node, byte *dest)
 {
     if (node->Flags == (TreeNodeFlags.PageRef))
     {
         var overFlowPage = tx.GetPage(node->PageNumber);
         Memory.Copy(dest, overFlowPage.Pointer + Constants.TreePageHeaderSize, overFlowPage.OverflowSize);
     }
     Memory.Copy(dest, (byte *)node + node->KeySize + Constants.NodeHeaderSize, node->DataSize);
 }
Пример #10
0
 public static byte *DirectAccess(LowLevelTransaction tx, TreeNodeHeader *node)
 {
     if (node->Flags == (TreeNodeFlags.PageRef))
     {
         var overFlowPage = tx.GetReadOnlyTreePage(node->PageNumber);
         return(overFlowPage.Base + Constants.TreePageHeaderSize);
     }
     return((byte *)node + node->KeySize + Constants.NodeHeaderSize);
 }
Пример #11
0
 public int GetDataSize(TreeNodeHeader *node)
 {
     if (node->Flags == (TreeNodeFlags.PageRef))
     {
         var overFlowPage = GetReadOnlyPage(node->PageNumber);
         return(overFlowPage.OverflowSize);
     }
     return(node->DataSize);
 }
Пример #12
0
        public byte *DirectAccessFromHeader(TreeNodeHeader *node)
        {
            if (node->Flags == TreeNodeFlags.PageRef)
            {
                var overFlowPage = GetReadOnlyTreePage(node->PageNumber);
                return(overFlowPage.Base + Constants.Tree.PageHeaderSize);
            }

            return((byte *)node + node->KeySize + Constants.Tree.NodeHeaderSize);
        }
Пример #13
0
        internal TreePage FindPageFor(Slice key, out TreeNodeHeader *node, out Func <TreeCursor> cursor)
        {
            TreePage p;

            if (TryUseRecentTransactionPage(key, out cursor, out p, out node))
            {
                return(p);
            }

            return(SearchForPage(key, out cursor, out node));
        }
Пример #14
0
        public ValueReader GetValueReaderFromHeader(TreeNodeHeader *node)
        {
            if (node->Flags == TreeNodeFlags.PageRef)
            {
                var overFlowPage = GetReadOnlyPage(node->PageNumber);

                Debug.Assert(overFlowPage.IsOverflow, "Requested overflow page but got " + overFlowPage.Flags);
                Debug.Assert(overFlowPage.OverflowSize > 0, "Overflow page cannot be size equal 0 bytes");

                return(new ValueReader(overFlowPage.Pointer + Constants.Tree.PageHeaderSize, overFlowPage.OverflowSize));
            }
            return(new ValueReader((byte *)node + node->KeySize + Constants.Tree.NodeHeaderSize, node->DataSize));
        }
Пример #15
0
 public static Slice GetData(LowLevelTransaction tx, TreeNodeHeader *node)
 {
     if (node->Flags == (TreeNodeFlags.PageRef))
     {
         var overFlowPage = tx.GetPage(node->PageNumber);
         if (overFlowPage.OverflowSize > ushort.MaxValue)
         {
             throw new InvalidOperationException("Cannot convert big data to a slice, too big");
         }
         return(Slice.External(tx.Allocator, overFlowPage.Pointer + Constants.TreePageHeaderSize, (ushort)overFlowPage.OverflowSize));
     }
     return(Slice.External(tx.Allocator, (byte *)node + node->KeySize + Constants.NodeHeaderSize, (ushort)node->DataSize));
 }
Пример #16
0
        public static int NodeEntry(TreeNodeHeader *other)
        {
            var sz = other->KeySize + Constants.NodeHeaderSize;

            if (other->Flags == TreeNodeFlags.Data || other->Flags == TreeNodeFlags.MultiValuePageRef)
            {
                sz += other->DataSize;
            }

            sz += sz & 1;

            return(sz);
        }
Пример #17
0
        public static ValueReader Reader(LowLevelTransaction tx, TreeNodeHeader *node)
        {
            if (node->Flags == (TreeNodeFlags.PageRef))
            {
                var overFlowPage = tx.GetPage(node->PageNumber);

                Debug.Assert(overFlowPage.IsOverflow, "Requested overflow page but got " + overFlowPage.Flags);
                Debug.Assert(overFlowPage.OverflowSize > 0, "Overflow page cannot be size equal 0 bytes");

                return(new ValueReader(overFlowPage.Pointer + Constants.Tree.PageHeaderSize, overFlowPage.OverflowSize));
            }
            return(new ValueReader((byte *)node + node->KeySize + Constants.Tree.NodeHeaderSize, node->DataSize));
        }
Пример #18
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);
        }
Пример #19
0
        public static int NodeEntryWithAnotherKey(TreeNodeHeader *other, Slice key)
        {
            var keySize = key.HasValue ? key.Size : other->KeySize;
            var sz      = keySize + Constants.NodeHeaderSize;

            if (other->Flags == TreeNodeFlags.Data || other->Flags == TreeNodeFlags.MultiValuePageRef)
            {
                sz += other->DataSize;
            }

            sz += sz & 1;

            return(sz);
        }
Пример #20
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));
        }
Пример #21
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);
        }
Пример #22
0
        private int AdjustSplitPosition(int currentIndex, int splitIndex, ref bool toRight)
        {
            Slice keyToInsert = _newKey;

            int pageSize = TreeSizeOf.NodeEntry(AbstractPager.PageMaxSpace, keyToInsert, _len) + Constants.Tree.NodeOffsetSize;

            if (toRight == false)
            {
                for (int i = 0; i < splitIndex; i++)
                {
                    TreeNodeHeader *node = _page.GetNode(i);
                    pageSize += node->GetNodeSize();
                    pageSize += pageSize & 1;
                    if (pageSize > _page.PageMaxSpace)
                    {
                        if (i <= currentIndex)
                        {
                            if (i < currentIndex)
                            {
                                toRight = true;
                            }
                            return(currentIndex);
                        }
                        return(i);
                    }
                }
            }
            else
            {
                for (int i = _page.NumberOfEntries - 1; i >= splitIndex; i--)
                {
                    TreeNodeHeader *node = _page.GetNode(i);
                    pageSize += node->GetNodeSize();
                    pageSize += pageSize & 1;
                    if (pageSize > _page.PageMaxSpace)
                    {
                        if (i >= currentIndex)
                        {
                            toRight = false;
                            return(currentIndex);
                        }
                        return(i + 1);
                    }
                }
            }

            return(splitIndex);
        }
Пример #23
0
        internal TreePage FindPageFor(Slice key, out TreeNodeHeader *node, out Func <Slice, TreeCursor> cursor, bool allowCompressed = false)
        {
            TreePage p;

            if (TryUseRecentTransactionPage(key, out cursor, out p, out node))
            {
                if (allowCompressed == false && p.IsCompressed)
                {
                    ThrowOnCompressedPage(p);
                }

                return(p);
            }

            return(SearchForPage(key, allowCompressed, out cursor, out node));
        }
Пример #24
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);
        }
Пример #25
0
                public TreeNodeHeaderSafe(Tree tree, TreeNodeHeader *nodeHeader)
                {
                    _tree       = tree ?? throw new ArgumentNullException(nameof(tree));
                    _nodeHeader = nodeHeader;

                    using (TreeNodeHeader.ToSlicePtr(tree.Llt.Allocator, nodeHeader, out Slice keySlice))
                    {
                        // uncomment to convert the slice to long
                        //if (keySlice.Size == sizeof(long))
                        //{
                        //    var idPtr = (long*)keySlice.Content.Ptr;
                        //    var id = Bits.SwapBytes(*idPtr);
                        //    Key = id.ToString();
                        //}
                        //else
                        Key = keySlice.ToString();
                    }
                }
Пример #26
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
            });
        }
Пример #27
0
        private Tree OpenMultiValueTree(Slice key, TreeNodeHeader *item)
        {
            Tree tree;

            if (_tx.TryGetMultiValueTree(this, key, out tree))
            {
                return(tree);
            }

            var childTreeHeader =
                (TreeRootHeader *)((byte *)item + item->KeySize + Constants.Tree.NodeHeaderSize);

            Debug.Assert(childTreeHeader->RootPageNumber < _llt.State.NextPageNumber);
            Debug.Assert(childTreeHeader->Flags == TreeFlags.MultiValue);

            tree = Open(_llt, _tx, key, childTreeHeader);
            _tx.AddMultiValueTree(this, key, tree);
            return(tree);
        }
Пример #28
0
        public Slice GetData(TreeNodeHeader *node)
        {
            Slice outputDataSlice;

            if (node->Flags == TreeNodeFlags.PageRef)
            {
                var overFlowPage = GetReadOnlyPage(node->PageNumber);
                if (overFlowPage.OverflowSize > ushort.MaxValue)
                {
                    throw new InvalidOperationException("Cannot convert big data to a slice, too big");
                }
                Slice.External(Llt.Allocator, overFlowPage.Pointer + Constants.Tree.PageHeaderSize,
                               (ushort)overFlowPage.OverflowSize, out outputDataSlice);
            }
            else
            {
                Slice.External(Llt.Allocator, (byte *)node + node->KeySize + Constants.Tree.NodeHeaderSize,
                               (ushort)node->DataSize, out outputDataSlice);
            }

            return(outputDataSlice);
        }
Пример #29
0
        private bool TryUseRecentTransactionPage(Slice key, out TreePage page, out TreeNodeHeader *node)
        {
            node = null;
            page = null;

            var recentPages = _recentlyFoundPages;

            if (recentPages == null)
            {
                return(false);
            }

            var foundPage = recentPages.Find(key);

            if (foundPage == null)
            {
                return(false);
            }

            if (foundPage.Page != null)
            {
                // we can't share the same instance, Page instance may be modified by
                // concurrently run iterators
                page = new TreePage(foundPage.Page.Base, foundPage.Page.Source, foundPage.Page.PageSize);
            }
            else
            {
                page = _llt.GetReadOnlyTreePage(foundPage.Number);
            }

            if (page.IsLeaf == false)
            {
                throw new InvalidDataException("Index points to a non leaf page");
            }

            node = page.Search(_llt, key); // will set the LastSearchPosition

            return(true);
        }
Пример #30
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);
        }