Esempio n. 1
0
        public List <long> GetAllIdsInSectionContaining(long id)
        {
            if (Contains(id) == false)
            {
                var posInPage           = (int)(id % Constants.Storage.PageSize);
                var pageNumberInSection = (id - posInPage) / Constants.Storage.PageSize;
                var pageHeaderForId     = PageHeaderFor(pageNumberInSection);

                // this is in another section, cannot free it directly, so we'll forward to the right section
                var sectionPageNumber = pageHeaderForId->PageNumber - pageHeaderForId->PageNumberInSection - 1;
                var actualSection     = new RawDataSection(_tx, sectionPageNumber);
                if (actualSection._sectionHeader->SectionOwnerHash != _sectionHeader->SectionOwnerHash)
                {
                    VoronUnrecoverableErrorException.Raise(_tx.Environment,
                                                           $"Cannot get all ids in section containing {id} because the raw data section starting in {sectionPageNumber} belongs to a different owner");
                }
                return(actualSection.GetAllIdsInSectionContaining(id));
            }

            var ids = new List <long>(_sectionHeader->NumberOfEntries);

            for (int i = 0; i < _sectionHeader->NumberOfPages && ids.Count < _sectionHeader->NumberOfEntries; i++)
            {
                FillAllIdsInPage(_sectionHeader->PageNumber + i, ids);
            }
            return(ids);
        }
Esempio n. 2
0
        public IIterator MultiRead(Slice key)
        {
            TreeNodeHeader *node;
            var             page = FindPageFor(key, out node);

            if (page == null || page.LastMatch != 0)
            {
                return(new EmptyIterator());
            }

            Debug.Assert(node != null);

            Slice fetchedNodeKey;

            using (TreeNodeHeader.ToSlicePtr(_llt.Allocator, node, out fetchedNodeKey))
            {
                if (SliceComparer.Equals(fetchedNodeKey, key) == false)
                {
                    VoronUnrecoverableErrorException.Raise(_llt, "Was unable to retrieve the correct node. Data corruption possible");
                }
            }

            if (node->Flags == TreeNodeFlags.MultiValuePageRef)
            {
                var tree = OpenMultiValueTree(key, node);

                return(tree.Iterate(true));
            }

            var ptr        = DirectAccessFromHeader(node);
            var nestedPage = new TreePage(ptr, (ushort)GetDataSize(node));

            return(new TreePageIterator(_llt, key, this, nestedPage));
        }
Esempio n. 3
0
        public static byte *DirectRead(LowLevelTransaction tx, long id, out int size)
        {
            var posInPage           = (int)(id % Constants.Storage.PageSize);
            var pageNumberInSection = (id - posInPage) / Constants.Storage.PageSize;
            var pageHeader          = PageHeaderFor(tx, pageNumberInSection);

            if (posInPage >= pageHeader->NextAllocation)
            {
                if (posInPage == 0)
                {
                    VoronUnrecoverableErrorException.Raise(tx.Environment,
                                                           $"Asked to load a large value from a raw data section page {pageHeader->PageNumber}, this is a bug");
                }

                VoronUnrecoverableErrorException.Raise(tx.Environment,
                                                       $"Asked to load a past the allocated values: {id} from page {pageHeader->PageNumber}");
            }

            var sizes = (RawDataEntrySizes *)((byte *)pageHeader + posInPage);

            if (sizes->UsedSize < 0)
            {
                VoronUnrecoverableErrorException.Raise(tx.Environment,
                                                       $"Asked to load a value that was already freed: {id} from page {pageHeader->PageNumber}");
            }

            if (sizes->AllocatedSize < sizes->UsedSize)
            {
                VoronUnrecoverableErrorException.Raise(tx.Environment,
                                                       $"Asked to load a value that where the allocated size is smaller than the used size: {id} from page {pageHeader->PageNumber}");
            }

            size = sizes->UsedSize;
            return((byte *)pageHeader + posInPage + sizeof(RawDataEntrySizes));
        }
Esempio n. 4
0
        private void SplitWrites(List <OutstandingWrite> writes)
        {
            for (var index = 0; index < writes.Count; index++)
            {
                var write = writes[index];
                try
                {
                    _cancellationToken.ThrowIfCancellationRequested();

                    using (var tx = _env.NewTransaction(TransactionFlags.ReadWrite))
                    {
                        HandleOperations(tx, new List <OutstandingWrite> {
                            write
                        }, _cancellationToken);
                        tx.Commit();
                        write.Completed();
                    }
                }
                catch (Exception e)
                {
                    if (e is SEHException)
                    {
                        e = new VoronUnrecoverableErrorException("Error occurred during write", new Win32Exception(error: e.HResult));
                    }

                    write.Errored(e);
                }
            }
        }
Esempio n. 5
0
        public static void TryThrowingBetterException(SystemException e, LuceneVoronDirectory directory)
        {
            if (e.Message.StartsWith("this writer hit an OutOfMemoryError"))
            {
                ThrowOutOfMemoryException();
            }

            if (e is Win32Exception win32Exception && win32Exception.IsOutOfMemory())
            {
                ThrowOutOfMemoryException();
            }

            if (e.InnerException is VoronUnrecoverableErrorException)
            {
                VoronUnrecoverableErrorException.Raise("Index data is corrupted", e);
            }

            if (e.IsOutOfDiskSpaceException())
            {
                // this commit stage is written to the temp scratch buffers
                var fullPath  = directory.TempFullPath;
                var driveInfo = DiskSpaceChecker.GetDiskSpaceInfo(fullPath);
                var freeSpace = driveInfo != null?driveInfo.TotalFreeSpace.ToString() : "N/A";

                throw new DiskFullException($"There isn't enough space to commit the index to {fullPath}. " +
                                            $"Currently available space: {freeSpace}", e);
            }

            void ThrowOutOfMemoryException()
            {
                throw new OutOfMemoryException("Index writer hit OOM during commit", e);
            }
        }
Esempio n. 6
0
 private void ThrowSchemaUpgradeRequired(int schemaVersionVal, string message)
 {
     VoronUnrecoverableErrorException.Raise(this,
                                            "The schema version of this database is expected to be " +
                                            Options.SchemaVersion + " but is actually " + schemaVersionVal +
                                            ". " + message);
 }
Esempio n. 7
0
        public void DeleteSection(long sectionPageNumber)
        {
            if (_tx.Flags == TransactionFlags.Read)
            {
                ThrowReadOnlyTransaction(sectionPageNumber);
            }

            if (sectionPageNumber != _sectionHeader->PageNumber)
            {
                // this is in another section, cannot delete it directly, so we'll forward to the right section
                var actualSection = new RawDataSection(_tx, sectionPageNumber);
                if (actualSection._sectionHeader->SectionOwnerHash != _sectionHeader->SectionOwnerHash)
                {
                    VoronUnrecoverableErrorException.Raise(_tx.Environment,
                                                           $"Cannot delete section because the raw data section starting in {sectionPageNumber} belongs to a different owner");
                }
                actualSection.DeleteSection(sectionPageNumber);
                return;
            }

            for (int i = 0; i < _sectionHeader->NumberOfPages; i++)
            {
                _tx.FreePage(_sectionHeader->PageNumber + i + 1);
            }
            _tx.FreePage(_sectionHeader->PageNumber);
        }
Esempio n. 8
0
        public long MultiCount(Slice key)
        {
            TreeNodeHeader *node;
            var             page = FindPageFor(key, out node);

            if (page == null || page.LastMatch != 0)
            {
                return(0);
            }

            Debug.Assert(node != null);

            Slice fetchedNodeKey;

            using (TreeNodeHeader.ToSlicePtr(_llt.Allocator, node, out fetchedNodeKey))
            {
                if (SliceComparer.Equals(fetchedNodeKey, key) == false)
                {
                    VoronUnrecoverableErrorException.Raise(_llt.Environment, "Was unable to retrieve the correct node. Data corruption possible");
                }
            }

            if (node->Flags == TreeNodeFlags.MultiValuePageRef)
            {
                var tree = OpenMultiValueTree(key, node);

                return(tree.State.NumberOfEntries);
            }

            var nestedPage = new TreePage(DirectAccessFromHeader(node), (ushort)GetDataSize(node));

            return(nestedPage.NumberOfEntries);
        }
Esempio n. 9
0
        private void EnsureNoDuplicateTransactionId(long id)
        {
            foreach (var journalFile in _journal.Files)
            {
                var lastSeenTxIdByJournal = journalFile.PageTranslationTable.GetLastSeenTransactionId();

                if (id <= lastSeenTxIdByJournal)
                {
                    VoronUnrecoverableErrorException.Raise(_env,
                                                           $"PTT of journal {journalFile.Number} already contains records for a new write tx. " +
                                                           $"Tx id = {id}, last seen by journal = {lastSeenTxIdByJournal}");
                }

                if (journalFile.PageTranslationTable.IsEmpty)
                {
                    continue;
                }

                var maxTxIdInJournal = journalFile.PageTranslationTable.MaxTransactionId();

                if (id <= maxTxIdInJournal)
                {
                    VoronUnrecoverableErrorException.Raise(_env,
                                                           $"PTT of journal {journalFile.Number} already contains records for a new write tx. " +
                                                           $"Tx id = {id}, max id in journal = {maxTxIdInJournal}");
                }
            }
        }
Esempio n. 10
0
        private bool TryUseRecentTransactionPage(Slice key, out TreeCursorConstructor cursor, out TreePage page, out TreeNodeHeader *node)
        {
            var foundPage = _recentlyFoundPages?.Find(key);

            if (foundPage == null)
            {
                page   = null;
                node   = null;
                cursor = default(TreeCursorConstructor);
                return(false);
            }

            var lastFoundPageNumber = foundPage.Number;

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

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

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

            cursor = new TreeCursorConstructor(_llt, this, page, foundPage.CursorPath, lastFoundPageNumber);
            return(true);
        }
Esempio n. 11
0
        protected void ThrowOnInvalidPageNumber(long pageNumber)
        {
            // this is a separate method because we don't want to have an exception throwing in the hot path

            VoronUnrecoverableErrorException.Raise(_options,
                "The page " + pageNumber + " was not allocated, allocated pages: " + NumberOfAllocatedPages + " in " +
                GetSourceName());
        }
Esempio n. 12
0
        private void HandleActualWrites(OutstandingWrite mine, CancellationToken token)
        {
            List <OutstandingWrite> writes = null;

            try
            {
                writes = BuildBatchGroup(mine);
                var completedSuccessfully = false;

                using (var tx = _env.NewTransaction(TransactionFlags.ReadWrite))
                {
                    HandleOperations(tx, writes, _cancellationToken);

                    try
                    {
                        tx.Commit();
                        if (ShouldRecordToDebugJournal)
                        {
                            _debugJournal.Flush();
                        }

                        completedSuccessfully = true;
                    }
                    catch (Exception e)
                    {
                        if (e is SEHException)
                        {
                            e = new VoronUnrecoverableErrorException("Error occurred during write", new Win32Exception(error: e.HResult));
                        }

                        // if we have an error during the commit, we can't recover, just fail them all.
                        foreach (var write in writes)
                        {
                            write.Errored(e);
                        }
                    }
                }

                if (completedSuccessfully)
                {
                    foreach (var write in writes)
                    {
                        write.Completed();
                    }
                }
            }
            catch (Exception e)
            {
                if (e is SEHException)
                {
                    e = new VoronUnrecoverableErrorException("Error occurred during write", new Win32Exception(error: e.HResult));
                }

                HandleWriteFailure(writes, mine, e);
            }
        }
Esempio n. 13
0
        public double Free(long id)
        {
            if (_tx.Flags == TransactionFlags.Read)
            {
                ThrowReadOnlyTransaction(id);
            }

            var posInPage           = (int)(id % Constants.Storage.PageSize);
            var pageNumberInSection = (id - posInPage) / Constants.Storage.PageSize;
            var pageHeader          = PageHeaderFor(pageNumberInSection);

            if (Contains(id) == false)
            {
                // this is in another section, cannot free it directly, so we'll forward to the right section
                var sectionPageNumber = pageHeader->PageNumber - pageHeader->PageNumberInSection - 1;
                var actualSection     = new RawDataSection(_tx, sectionPageNumber);
                if (actualSection.Contains(id) == false)
                {
                    VoronUnrecoverableErrorException.Raise(_tx.Environment, $"Cannot delete {id} because the raw data section starting in {sectionPageNumber} with size {actualSection.AllocatedSize} doesn't own it. Possible data corruption?");
                }

                if (actualSection._sectionHeader->SectionOwnerHash != _sectionHeader->SectionOwnerHash)
                {
                    VoronUnrecoverableErrorException.Raise(_tx.Environment,
                                                           $"Cannot delete {id} because the raw data section starting in {sectionPageNumber} belongs to a different owner");
                }

                return(actualSection.Free(id));
            }

            pageHeader = ModifyPage(pageHeader);
            if (posInPage >= pageHeader->NextAllocation)
            {
                VoronUnrecoverableErrorException.Raise(_tx.Environment, $"Asked to load a past the allocated values: {id} from page {pageHeader->PageNumber}");
            }

            var sizes = (RawDataEntrySizes *)((byte *)pageHeader + posInPage);

            if (sizes->UsedSize < 0)
            {
                VoronUnrecoverableErrorException.Raise(_tx.Environment, $"Asked to free a value that was already freed: {id} from page {pageHeader->PageNumber}");
            }

            sizes->UsedSize = -1;
            Memory.Set((byte *)pageHeader + posInPage + sizeof(RawDataEntrySizes), 0, sizes->AllocatedSize);
            pageHeader->NumberOfEntries--;

            EnsureHeaderModified();
            _sectionHeader->NumberOfEntries--;
            var sizeFreed = sizes->AllocatedSize + (sizeof(short) * 2);

            _sectionHeader->AllocatedSize -= sizeFreed;
            AvailableSpace[pageHeader->PageNumberInSection] += (ushort)sizeFreed;

            return(Density);
        }
Esempio n. 14
0
 private void ValidateWritablePages()
 {
     foreach (var writableKey in writablePages)
     {
         long pageNumber = writableKey.Key;
         if (!dirtyPagesValidate.Contains(pageNumber))
         {
             VoronUnrecoverableErrorException.Raise(_env, "Writable key is not dirty (which means you are asking for a page modification for no reason).");
         }
     }
 }
Esempio n. 15
0
        private static MultiValuesReport CreateMultiValuesReport(Tree tree)
        {
            var multiValues = new MultiValuesReport();

            using (var multiTreeIterator = tree.Iterate(false))
            {
                if (multiTreeIterator.Seek(Slices.BeforeAllKeys))
                {
                    do
                    {
                        var currentNode = multiTreeIterator.Current;

                        switch (currentNode->Flags)
                        {
                        case TreeNodeFlags.MultiValuePageRef:
                        {
                            var multiValueTreeHeader = (TreeRootHeader *)((byte *)currentNode + currentNode->KeySize + Constants.Tree.NodeHeaderSize);

                            Debug.Assert(multiValueTreeHeader->Flags == TreeFlags.MultiValue);

                            multiValues.NumberOfEntries += multiValueTreeHeader->NumberOfEntries;
                            multiValues.BranchPages     += multiValueTreeHeader->BranchPages;
                            multiValues.LeafPages       += multiValueTreeHeader->LeafPages;
                            multiValues.PageCount       += multiValueTreeHeader->PageCount;
                            break;
                        }

                        case TreeNodeFlags.Data:
                        {
                            var nestedPage = GetNestedMultiValuePage(tree, tree.DirectAccessFromHeader(currentNode), currentNode);

                            multiValues.NumberOfEntries += nestedPage.NumberOfEntries;
                            break;
                        }

                        case TreeNodeFlags.PageRef:
                        {
                            var overFlowPage = tree.GetReadOnlyTreePage(currentNode->PageNumber);
                            var nestedPage   = GetNestedMultiValuePage(tree, overFlowPage.Base + Constants.Tree.PageHeaderSize, currentNode);

                            multiValues.NumberOfEntries += nestedPage.NumberOfEntries;
                            break;
                        }

                        default:
                            VoronUnrecoverableErrorException.Raise(tree.Llt, "currentNode->FixedTreeFlags has value of " + currentNode->Flags);
                            break;
                        }
                    } while (multiTreeIterator.MoveNext());
                }
            }
            return(multiValues);
        }
Esempio n. 16
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);
        }
Esempio n. 17
0
        public List <long> GetAllIdsInSectionContaining(long id)
        {
            if (Contains(id) == false)
            {
                var posInPage           = (int)(id % Constants.Storage.PageSize);
                var pageNumberInSection = (id - posInPage) / Constants.Storage.PageSize;
                var pageHeaderForId     = PageHeaderFor(pageNumberInSection);

                // this is in another section, cannot free it directly, so we'll forward to the right section
                var sectionPageNumber = pageHeaderForId->PageNumber - pageHeaderForId->PageNumberInSection - 1;
                var actualSection     = new RawDataSection(_tx, sectionPageNumber);
                if (actualSection._sectionHeader->SectionOwnerHash != _sectionHeader->SectionOwnerHash)
                {
                    VoronUnrecoverableErrorException.Raise(_tx.Environment,
                                                           $"Cannot get all ids in section containing {id} because the raw data section starting in {sectionPageNumber} belongs to a different owner");
                }
                return(actualSection.GetAllIdsInSectionContaining(id));
            }

            var ids = new List <long>(_sectionHeader->NumberOfEntries);

            for (int i = 0; i < _sectionHeader->NumberOfPages && ids.Count < _sectionHeader->NumberOfEntries; i++)
            {
                var pageHeader = PageHeaderFor(_sectionHeader->PageNumber + i + 1);
                var offset     = sizeof(RawDataSmallPageHeader);
                while (offset < Constants.Storage.PageSize)
                {
                    var sizes = (RawDataEntrySizes *)((byte *)pageHeader + offset);
                    if (sizes->UsedSize != -1)
                    {
                        var currentId = (pageHeader->PageNumber * Constants.Storage.PageSize) + offset;

                        var posInPage = (int)(currentId % Constants.Storage.PageSize);

                        if (posInPage >= pageHeader->NextAllocation)
                        {
                            break;
                        }

                        ids.Add(currentId);

                        if (ids.Count == _sectionHeader->NumberOfEntries)
                        {
                            break;
                        }
                    }
                    offset += sizeof(short) * 2 + sizes->AllocatedSize;
                }
            }
            return(ids);
        }
Esempio n. 18
0
        public bool TryWriteDirect(long id, int size, out byte *writePos)
        {
            if (_tx.Flags == TransactionFlags.Read)
            {
                ThrowReadOnlyTransaction(id);
            }

            var posInPage           = (int)(id % Constants.Storage.PageSize);
            var pageNumberInSection = (id - posInPage) / Constants.Storage.PageSize;
            var pageHeader          = PageHeaderFor(pageNumberInSection);

            if (posInPage >= pageHeader->NextAllocation)
            {
                VoronUnrecoverableErrorException.Raise(_tx.Environment, $"Asked to load a past the allocated values: {id} from page {pageHeader->PageNumber}");
            }

            var sizes = (RawDataEntrySizes *)((byte *)pageHeader + posInPage);

            if (sizes->UsedSize < 0)
            {
                VoronUnrecoverableErrorException.Raise(_tx.Environment, $"Asked to load a value that was already freed: {id} from page {pageHeader->PageNumber}");
            }

            if (sizes->AllocatedSize < sizes->UsedSize)
            {
                VoronUnrecoverableErrorException.Raise(_tx.Environment,
                                                       "Asked to load a value that where the allocated size is smaller than the used size: " + id +
                                                       " from page " +
                                                       pageHeader->PageNumber);
            }

            if (sizes->AllocatedSize < size)
            {
                writePos = (byte *)0;
                return(false); // can't write here
            }


            pageHeader = ModifyPage(pageHeader);
            writePos   = ((byte *)pageHeader + posInPage + sizeof(short) /*allocated*/ + sizeof(short) /*used*/);
            // note that we have to do this calc again, pageHeader might have changed
            ((RawDataEntrySizes *)((byte *)pageHeader + posInPage))->UsedSize = (short)size;

            return(true);
        }
Esempio n. 19
0
        internal static string ReadDbId(UpdateStep step)
        {
            var metadataTree = step.WriteTx.ReadTree(Constants.MetadataTreeNameSlice);

            if (metadataTree == null)
            {
                VoronUnrecoverableErrorException.Raise(step.WriteTx.LowLevelTransaction,
                                                       "Could not find metadata tree in database, possible mismatch / corruption?");
            }

            Debug.Assert(metadataTree != null);
            // ReSharper disable once PossibleNullReferenceException
            var dbId = metadataTree.Read("db-id");

            if (dbId == null)
            {
                VoronUnrecoverableErrorException.Raise(step.WriteTx.LowLevelTransaction,
                                                       "Could not find db id in metadata tree, possible mismatch / corruption?");
            }

            var buffer = new byte[16];

            Debug.Assert(dbId != null);
            // ReSharper disable once PossibleNullReferenceException
            var dbIdBytes = dbId.Reader.Read(buffer, 0, 16);

            if (dbIdBytes != 16)
            {
                VoronUnrecoverableErrorException.Raise(step.WriteTx.LowLevelTransaction,
                                                       "The db id value in metadata tree wasn't 16 bytes in size, possible mismatch / corruption?");
            }

            var databaseGuidId = new Guid(buffer);
            var dbIdStr        = new string(' ', 22);

            fixed(char *pChars = dbIdStr)
            {
                var result = Base64.ConvertToBase64ArrayUnpadded(pChars, (byte *)&databaseGuidId, 0, 16);

                Debug.Assert(result == 22);
            }

            return(dbIdStr);
        }
Esempio n. 20
0
        private void ValidateReadOnlyPages()
        {
            foreach (var readOnlyKey in readOnlyPages)
            {
                long pageNumber = readOnlyKey.Key;
                if (_dirtyPages.Contains(pageNumber))
                {
                    VoronUnrecoverableErrorException.Raise(_env, "Read only page is dirty (which means you are modifying a page directly in the data -- non transactionally -- ).");
                }

                var page = this.GetPage(pageNumber);

                ulong pageHash = Hashing.XXHash64.Calculate(page.Pointer, (ulong)Environment.Options.PageSize);
                if (pageHash != readOnlyKey.Value)
                {
                    VoronUnrecoverableErrorException.Raise(_env, "Read only page content is different (which means you are modifying a page directly in the data -- non transactionally -- ).");
                }
            }
        }
Esempio n. 21
0
        private void ValidateReadOnlyPages()
        {
            foreach (var readOnlyKey in readOnlyPages)
            {
                long pageNumber = readOnlyKey.Key;
                if (dirtyPagesValidate.Contains(pageNumber))
                {
                    VoronUnrecoverableErrorException.Raise(_env, "Read only page is dirty (which means you are modifying a page directly in the data -- non transactionally -- ).");
                }

                var page = GetPage(pageNumber);

                ulong pageHash = StorageEnvironment.CalculatePageChecksum(page.Pointer, page.PageNumber, page.Flags, page.OverflowSize);
                if (pageHash != readOnlyKey.Value)
                {
                    VoronUnrecoverableErrorException.Raise(_env, "Read only page content is different (which means you are modifying a page directly in the data -- non transactionally -- ).");
                }
            }
        }
Esempio n. 22
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.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);
        }
Esempio n. 23
0
        public bool Update(UpdateStep step)
        {
            try
            {
                var tx = step.ReadTx;
                using (var it = tx.LowLevelTransaction.RootObjects.Iterate(false))
                {
                    if (it.Seek(Slices.BeforeAllKeys) == false)
                    {
                        return(true);
                    }
                    do
                    {
                        var rootObjectType = tx.GetRootObjectType(it.CurrentKey);
                        if (rootObjectType == RootObjectType.FixedSizeTree)
                        {
                            tx.FixedTreeFor(it.CurrentKey).ValidateTree_Forced();
                        }
                        else if (rootObjectType == RootObjectType.Table)
                        {
                            var tableTree             = tx.ReadTree(it.CurrentKey, RootObjectType.Table);
                            var writtenSchemaData     = tableTree.DirectRead(TableSchema.SchemasSlice);
                            var writtenSchemaDataSize = tableTree.GetDataSize(TableSchema.SchemasSlice);
                            var schema = TableSchema.ReadFrom(tx.Allocator, writtenSchemaData, writtenSchemaDataSize);
                            new Table(schema, it.CurrentKey, tx, tableTree, schema.TableType).AssertValidFixedSizeTrees();
                        }
                    } while (it.MoveNext());
                }

                return(true);
            }
            catch (Exception e)
            {
                VoronUnrecoverableErrorException.Raise("Failed to update documents from version 13 to 14, due to an internal error. " +
                                                       "Use the Voron.Recovery tool to export this database and import it into a new database.", e);
            }

            return(false);
        }
Esempio n. 24
0
        private void TrackReadOnlyPage(Page page)
        {
            if (writablePages.ContainsKey(page.PageNumber))
            {
                return;
            }

            ulong pageHash = StorageEnvironment.CalculatePageChecksum(page.Pointer, page.PageNumber, page.Flags, page.OverflowSize);

            ulong storedHash;

            if (readOnlyPages.TryGetValue(page.PageNumber, out storedHash))
            {
                if (pageHash != storedHash)
                {
                    VoronUnrecoverableErrorException.Raise(_env, "Read Only Page has change between tracking requests. Page #" + page.PageNumber);
                }
            }
            else
            {
                readOnlyPages[page.PageNumber] = pageHash;
            }
        }
Esempio n. 25
0
 internal void BackgroundFlushWritesToDataFile()
 {
     try
     {
         _journal.Applicator.ApplyLogsToDataFile(_cancellationTokenSource.Token,
                                                 // we intentionally don't wait, if the flush lock is held, something else is flushing, so we don't need
                                                 // to hold the thread
                                                 TimeSpan.Zero);
     }
     catch (TimeoutException)
     {
         // we can ignore this, we'll try next time
     }
     catch (SEHException sehException)
     {
         VoronUnrecoverableErrorException.Raise(this, "Error occurred during flushing journals to the data file",
                                                new Win32Exception(sehException.HResult));
     }
     catch (Exception e)
     {
         VoronUnrecoverableErrorException.Raise(this, "Error occurred during flushing journals to the data file", e);
     }
 }
Esempio n. 26
0
        private void UpgradeSchemaIfRequired()
        {
            int schemaVersionVal;

            var readPersistentContext = new TransactionPersistentContext(true);

            using (var readTxInner = NewLowLevelTransaction(readPersistentContext, TransactionFlags.Read))
                using (var readTx = new Transaction(readTxInner))
                {
                    var metadataTree = readTx.ReadTree(Constants.MetadataTreeNameSlice);

                    var schemaVersion = metadataTree.Read("schema-version");
                    if (schemaVersion == null)
                    {
                        VoronUnrecoverableErrorException.Raise(this, "Could not find schema version in metadata tree, possible mismatch / corruption?");
                    }

                    schemaVersionVal = schemaVersion.Reader.ReadLittleEndianInt32();
                }

            if (Options.SchemaVersion != 0 &&
                schemaVersionVal != Options.SchemaVersion)
            {
                if (schemaVersionVal > Options.SchemaVersion)
                {
                    ThrowSchemaUpgradeRequired(schemaVersionVal, "You have a schema version is newer than the current supported version.");
                }

                Func <Transaction, Transaction, int, bool> upgrader = Options.SchemaUpgrader;
                if (upgrader == null)
                {
                    ThrowSchemaUpgradeRequired(schemaVersionVal, "You need to upgrade the schema but there is no schema uprader provided.");
                }

                UpgradeSchema(schemaVersionVal, upgrader);
            }
        }
Esempio n. 27
0
        private RawDataSmallPageHeader *DefragPage(RawDataSmallPageHeader *pageHeader)
        {
            pageHeader = ModifyPage(pageHeader);

            if (pageHeader->NumberOfEntries == 0)
            {
                pageHeader->NextAllocation = (ushort)sizeof(RawDataSmallPageHeader);
                Memory.Set((byte *)pageHeader + pageHeader->NextAllocation, 0,
                           Constants.Storage.PageSize - pageHeader->NextAllocation);

                return(pageHeader);
            }


            TemporaryPage tmp;

            using (_tx.Environment.GetTemporaryPage(_tx, out tmp))
            {
                var maxUsedPos = pageHeader->NextAllocation;
                Memory.Copy(tmp.TempPagePointer, (byte *)pageHeader, Constants.Storage.PageSize);

                pageHeader->NextAllocation = (ushort)sizeof(RawDataSmallPageHeader);
                Memory.Set((byte *)pageHeader + pageHeader->NextAllocation, 0,
                           Constants.Storage.PageSize - pageHeader->NextAllocation);

                pageHeader->NumberOfEntries = 0;
                var pos = pageHeader->NextAllocation;
                while (pos < maxUsedPos)
                {
                    var oldSize = (RawDataEntrySizes *)(tmp.TempPagePointer + pos);

                    if (oldSize->AllocatedSize <= 0)
                    {
                        VoronUnrecoverableErrorException.Raise(_tx, $"Allocated size cannot be zero or negative, but was {oldSize->AllocatedSize} in page {pageHeader->PageNumber}");
                    }

                    if (oldSize->UsedSize < 0)
                    {
                        pos += (ushort)(oldSize->AllocatedSize + sizeof(RawDataEntrySizes));
                        continue; // this was freed
                    }
                    var prevId = (pageHeader->PageNumber) * Constants.Storage.PageSize + pos;
                    var newId  = (pageHeader->PageNumber) * Constants.Storage.PageSize + pageHeader->NextAllocation;
                    if (prevId != newId)
                    {
                        OnDataMoved(prevId, newId, tmp.TempPagePointer + pos + sizeof(RawDataEntrySizes), oldSize->UsedSize);
                    }

                    var newSize = (RawDataEntrySizes *)(((byte *)pageHeader) + pageHeader->NextAllocation);
                    newSize->AllocatedSize      = oldSize->AllocatedSize;
                    newSize->UsedSize           = oldSize->UsedSize;
                    pageHeader->NextAllocation += (ushort)sizeof(RawDataEntrySizes);
                    pageHeader->NumberOfEntries++;
                    Memory.Copy(((byte *)pageHeader) + pageHeader->NextAllocation, tmp.TempPagePointer + pos + sizeof(RawDataEntrySizes),
                                oldSize->UsedSize);

                    pageHeader->NextAllocation += (ushort)oldSize->AllocatedSize;
                    pos += (ushort)(oldSize->AllocatedSize + sizeof(RawDataEntrySizes));
                }
            }
            return(pageHeader);
        }
Esempio n. 28
0
 private void ThrowStreamSizeMismatch(Slice name, long totalChunksSize, StreamInfo *info)
 {
     VoronUnrecoverableErrorException.Raise(_tx.LowLevelTransaction.Environment,
                                            $"Stream size mismatch of '{name}' stream. Sum of chunks size is {totalChunksSize} while stream info has {info->TotalSize}");
 }
Esempio n. 29
0
        private unsafe void LoadExistingDatabase()
        {
            var  header             = stackalloc TransactionHeader[1];
            bool hadIntegrityIssues = _journal.RecoverDatabase(header);

            if (hadIntegrityIssues)
            {
                var message = _journal.Files.Count == 0 ? "Unrecoverable database" : "Database recovered partially. Some data was lost.";

                _options.InvokeRecoveryError(this, message, null);
            }

            var entry          = _headerAccessor.CopyHeader();
            var nextPageNumber = (header->TransactionId == 0 ? entry.LastPageNumber : header->LastPageNumber) + 1;

            State = new StorageEnvironmentState(null, nextPageNumber)
            {
                NextPageNumber = nextPageNumber,
                Options        = Options
            };

            _transactionsCounter = (header->TransactionId == 0 ? entry.TransactionId : header->TransactionId);

            var transactionPersistentContext = new TransactionPersistentContext(true);

            using (var tx = NewLowLevelTransaction(transactionPersistentContext, TransactionFlags.ReadWrite))
            {
                using (var root = Tree.Open(tx, null, Constants.RootTreeNameSlice, header->TransactionId == 0 ? &entry.Root : &header->Root))
                {
                    tx.UpdateRootsIfNeeded(root);

                    using (var treesTx = new Transaction(tx))
                    {
                        var metadataTree = treesTx.ReadTree(Constants.MetadataTreeNameSlice);
                        if (metadataTree == null)
                        {
                            VoronUnrecoverableErrorException.Raise(this,
                                                                   "Could not find metadata tree in database, possible mismatch / corruption?");
                        }

                        var dbId = metadataTree.Read("db-id");
                        if (dbId == null)
                        {
                            VoronUnrecoverableErrorException.Raise(this,
                                                                   "Could not find db id in metadata tree, possible mismatch / corruption?");
                        }

                        var buffer    = new byte[16];
                        var dbIdBytes = dbId.Reader.Read(buffer, 0, 16);
                        if (dbIdBytes != 16)
                        {
                            VoronUnrecoverableErrorException.Raise(this,
                                                                   "The db id value in metadata tree wasn't 16 bytes in size, possible mismatch / corruption?");
                        }

                        var databseGuidId = _options.GenerateNewDatabaseId == false ? new Guid(buffer) : Guid.NewGuid();
                        DbId = databseGuidId;

                        FillBase64Id(databseGuidId);

                        if (_options.GenerateNewDatabaseId)
                        {
                            // save the new database id
                            metadataTree.Add("db-id", DbId.ToByteArray());
                        }

                        var schemaVersion = metadataTree.Read("schema-version");
                        if (schemaVersion == null)
                        {
                            VoronUnrecoverableErrorException.Raise(this,
                                                                   "Could not find schema version in metadata tree, possible mismatch / corruption?");
                        }

                        var schemaVersionVal = schemaVersion.Reader.ReadLittleEndianInt32();
                        if (Options.SchemaVersion != 0 &&
                            schemaVersionVal != Options.SchemaVersion)
                        {
                            VoronUnrecoverableErrorException.Raise(this,
                                                                   "The schema version of this database is expected to be " +
                                                                   Options.SchemaVersion + " but is actually " + schemaVersionVal +
                                                                   ". You need to upgrade the schema.");
                        }

                        tx.Commit();
                    }
                }
            }
        }
Esempio n. 30
0
        private RawDataSmallPageHeader *DefragPage(RawDataSmallPageHeader *pageHeader)
        {
            pageHeader = ModifyPage(pageHeader);

            if (pageHeader->NumberOfEntries == 0)
            {
                pageHeader->NextAllocation = (ushort)sizeof(RawDataSmallPageHeader);
                Memory.Set((byte *)pageHeader + pageHeader->NextAllocation, 0,
                           Constants.Storage.PageSize - pageHeader->NextAllocation);

                return(pageHeader);
            }

            using (_llt.Environment.GetTemporaryPage(_llt, out TemporaryPage tmp))
            {
                var maxUsedPos = pageHeader->NextAllocation;
                Memory.Copy(tmp.TempPagePointer, (byte *)pageHeader, Constants.Storage.PageSize);

                pageHeader->NextAllocation = (ushort)sizeof(RawDataSmallPageHeader);
                Memory.Set((byte *)pageHeader + pageHeader->NextAllocation, 0,
                           Constants.Storage.PageSize - pageHeader->NextAllocation);

                pageHeader->NumberOfEntries = 0;
                var pos = pageHeader->NextAllocation;

                while (pos < maxUsedPos)
                {
                    var oldSize = (RawDataEntrySizes *)(tmp.TempPagePointer + pos);

                    if (oldSize->AllocatedSize <= 0)
                    {
                        VoronUnrecoverableErrorException.Raise(_llt, $"Allocated size cannot be zero or negative, but was {oldSize->AllocatedSize} in page {pageHeader->PageNumber}");
                    }

                    if (oldSize->IsFreed)
                    {
                        pos += (ushort)(oldSize->AllocatedSize + sizeof(RawDataEntrySizes));
                        continue; // this was freed
                    }
                    var   prevId   = (pageHeader->PageNumber) * Constants.Storage.PageSize + pos;
                    var   newId    = (pageHeader->PageNumber) * Constants.Storage.PageSize + pageHeader->NextAllocation;
                    byte *entryPos = tmp.TempPagePointer + pos + sizeof(RawDataEntrySizes);
                    if (prevId != newId)
                    {
                        var size = oldSize->UsedSize;
                        if (oldSize->IsCompressed)
                        {
                            using var __ = Table.DecompressValue(_transaction, entryPos, size, out var buffer);
                            OnDataMoved(prevId, newId, buffer.Ptr, buffer.Length);
                        }
                        else
                        {
                            OnDataMoved(prevId, newId, entryPos, oldSize->UsedSize);
                        }
                    }

                    var newSize = (RawDataEntrySizes *)(((byte *)pageHeader) + pageHeader->NextAllocation);
                    newSize->AllocatedSize      = oldSize->AllocatedSize;
                    newSize->UsedSize_Buffer    = oldSize->UsedSize_Buffer;
                    pageHeader->NextAllocation += (ushort)sizeof(RawDataEntrySizes);
                    pageHeader->NumberOfEntries++;
                    Memory.Copy(((byte *)pageHeader) + pageHeader->NextAllocation, entryPos,
                                oldSize->UsedSize);

                    pageHeader->NextAllocation += (ushort)oldSize->AllocatedSize;
                    pos += (ushort)(oldSize->AllocatedSize + sizeof(RawDataEntrySizes));
                }
            }
            return(pageHeader);
        }