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