示例#1
0
        public EnvironmentStats Stats()
        {
            var transactionPersistentContext = new TransactionPersistentContext();

            using (var tx = NewLowLevelTransaction(transactionPersistentContext, TransactionFlags.Read))
            {
                var numberOfAllocatedPages = Math.Max(_dataPager.NumberOfAllocatedPages, State.NextPageNumber - 1); // async apply to data file task

                return(new EnvironmentStats
                {
                    FreePagesOverhead = FreeSpaceHandling.GetFreePagesOverhead(tx),
                    RootPages = tx.RootObjects.State.PageCount,
                    UnallocatedPagesAtEndOfFile = _dataPager.NumberOfAllocatedPages - NextPageNumber,
                    UsedDataFileSizeInBytes = (State.NextPageNumber - 1) * Constants.Storage.PageSize,
                    AllocatedDataFileSizeInBytes = numberOfAllocatedPages * Constants.Storage.PageSize,
                    NextWriteTransactionId = NextWriteTransactionId,
                    ActiveTransactions = ActiveTransactions.AllTransactions
                });
            }
        }
示例#2
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);
            }
        }
示例#3
0
        internal LowLevelTransaction NewLowLevelTransaction(TransactionPersistentContext transactionPersistentContext, TransactionFlags flags, ByteStringContext context = null, TimeSpan?timeout = null)
        {
            _cancellationTokenSource.Token.ThrowIfCancellationRequested();

            bool txLockTaken = false;
            bool flushInProgressReadLockTaken = false;

            try
            {
                IncrementUsageOnNewTransaction();

                if (flags == TransactionFlags.ReadWrite)
                {
                    var wait = timeout ?? (Debugger.IsAttached ? TimeSpan.FromMinutes(30) : TimeSpan.FromSeconds(30));

                    if (FlushInProgressLock.IsWriteLockHeld == false)
                    {
                        flushInProgressReadLockTaken = FlushInProgressLock.TryEnterReadLock(wait);
                    }

                    txLockTaken = _transactionWriter.Wait(wait);
                    if (txLockTaken == false || (flushInProgressReadLockTaken == false &&
                                                 FlushInProgressLock.IsWriteLockHeld == false))
                    {
                        GlobalFlushingBehavior.GlobalFlusher.Value.MaybeFlushEnvironment(this);
                        ThrowOnTimeoutWaitingForWriteTxLock(wait);
                    }
                    _cancellationTokenSource.Token.ThrowIfCancellationRequested();

                    _currentTransactionHolder = NativeMemory.ThreadAllocations.Value;
                    WriteTransactionStarted();

                    if (_endOfDiskSpace != null)
                    {
                        _endOfDiskSpace.AssertCanContinueWriting();

                        _endOfDiskSpace = null;
                        Task.Run(IdleFlushTimer);
                        GlobalFlushingBehavior.GlobalFlusher.Value.MaybeFlushEnvironment(this);
                    }
                }

                LowLevelTransaction tx;

                _txCommit.EnterReadLock();
                try
                {
                    _cancellationTokenSource.Token.ThrowIfCancellationRequested();

                    if (_currentTransactionHolder == null)
                    {
                        _currentTransactionHolder = NativeMemory.ThreadAllocations.Value;
                    }

                    long txId = flags == TransactionFlags.ReadWrite ? NextWriteTransactionId : CurrentReadTransactionId;
                    tx = new LowLevelTransaction(this, txId, transactionPersistentContext, flags, _freeSpaceHandling,
                                                 context)
                    {
                        FlushInProgressLockTaken = flushInProgressReadLockTaken,
                        CurrentTransactionHolder = _currentTransactionHolder
                    };
                    ActiveTransactions.Add(tx);
                }
                finally
                {
                    _txCommit.ExitReadLock();
                }

                var state = _dataPager.PagerState;
                tx.EnsurePagerStateReference(state);

                return(tx);
            }
            catch (Exception)
            {
                try
                {
                    if (txLockTaken)
                    {
                        _transactionWriter.Release();
                    }
                    if (flushInProgressReadLockTaken)
                    {
                        FlushInProgressLock.ExitReadLock();
                    }
                }
                finally
                {
                    DecrementUsageOnTransactionCreationFailure();
                }
                throw;
            }
        }
示例#4
0
        public Transaction WriteTransaction(TransactionPersistentContext transactionPersistentContext, ByteStringContext context = null)
        {
            var writeTransaction = new Transaction(NewLowLevelTransaction(transactionPersistentContext, TransactionFlags.ReadWrite, context, null));

            return(writeTransaction);
        }
示例#5
0
 public Transaction ReadTransaction(TransactionPersistentContext transactionPersistentContext, ByteStringContext context = null)
 {
     return(new Transaction(NewLowLevelTransaction(transactionPersistentContext, TransactionFlags.Read, context)));
 }
示例#6
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();
                    }
                }
            }
        }
示例#7
0
        public TransactionsModeResult SetTransactionMode(TransactionsMode mode, TimeSpan duration)
        {
            var transactionPersistentContext = new TransactionPersistentContext();

            using (var tx = NewLowLevelTransaction(transactionPersistentContext, TransactionFlags.ReadWrite))
            {
                var oldMode = Options.TransactionsMode;

                if (_log.IsOperationsEnabled)
                {
                    _log.Operations($"Setting transaction mode to {mode}. Old mode is {oldMode}");
                }

                if (oldMode == mode)
                {
                    return(TransactionsModeResult.ModeAlreadySet);
                }

                Options.TransactionsMode = mode;
                if (duration == TimeSpan.FromMinutes(0)) // infinite
                {
                    Options.NonSafeTransactionExpiration = null;
                }
                else
                {
                    Options.NonSafeTransactionExpiration = DateTime.Now + duration;
                }

                if (oldMode == TransactionsMode.Lazy)
                {
                    tx.IsLazyTransaction = false;
                    // we only commit here, the rest of the of the options are without
                    // commit and we use the tx lock
                    tx.Commit();
                }

                if (oldMode == TransactionsMode.Danger)
                {
                    Journal.TruncateJournal();
                    _dataPager.Sync(Journal.Applicator.TotalWrittenButUnsyncedBytes);
                }

                switch (mode)
                {
                case TransactionsMode.Safe:
                case TransactionsMode.Lazy:
                {
                    Options.PosixOpenFlags = Options.SafePosixOpenFlags;
                    Options.WinOpenFlags   = StorageEnvironmentOptions.SafeWin32OpenFlags;
                }
                break;

                case TransactionsMode.Danger:
                {
                    Options.PosixOpenFlags = Options.DefaultPosixFlags;
                    Options.WinOpenFlags   = Win32NativeFileAttributes.None;
                    Journal.TruncateJournal();
                }
                break;

                default:
                {
                    throw new InvalidOperationException("Query string value 'mode' is not a valid mode: " + mode);
                }
                }

                return(TransactionsModeResult.SetModeSuccessfully);
            }
        }
        internal LowLevelTransaction NewLowLevelTransaction(TransactionPersistentContext transactionPersistentContext, TransactionFlags flags, ByteStringContext context = null, TimeSpan?timeout = null)
        {
            bool txLockTaken = false;
            bool flushInProgressReadLockTaken = false;

            try
            {
                if (flags == TransactionFlags.ReadWrite)
                {
                    var wait = timeout ?? (Debugger.IsAttached ? TimeSpan.FromMinutes(30) : TimeSpan.FromSeconds(30));

                    if (FlushInProgressLock.IsWriteLockHeld == false)
                    {
                        flushInProgressReadLockTaken = FlushInProgressLock.TryEnterReadLock(wait);
                    }
                    if (Monitor.IsEntered(_txWriter))
                    {
                        ThrowOnRecursiveWriteTransaction();
                    }
                    Monitor.TryEnter(_txWriter, wait, ref txLockTaken);
                    if (txLockTaken == false || (flushInProgressReadLockTaken == false && FlushInProgressLock.IsWriteLockHeld == false))
                    {
                        GlobalFlushingBehavior.GlobalFlusher.Value.MaybeFlushEnvironment(this);
                        ThrowOnTimeoutWaitingForWriteTxLock(wait);
                    }

                    _writeTransactionRunning.SetByAsyncCompletion();

                    if (_endOfDiskSpace != null)
                    {
                        if (_endOfDiskSpace.CanContinueWriting)
                        {
                            CatastrophicFailure      = null;
                            _endOfDiskSpace          = null;
                            _cancellationTokenSource = new CancellationTokenSource();
                            Task.Run(IdleFlushTimer);
                            GlobalFlushingBehavior.GlobalFlusher.Value.MaybeFlushEnvironment(this);
                        }
                    }
                }

                LowLevelTransaction tx;

                _txCommit.EnterReadLock();
                try
                {
                    long txId = flags == TransactionFlags.ReadWrite ? _transactionsCounter + 1 : _transactionsCounter;
                    tx = new LowLevelTransaction(this, txId, transactionPersistentContext, flags, _freeSpaceHandling,
                                                 context)
                    {
                        FlushInProgressLockTaken = flushInProgressReadLockTaken
                    };
                    ActiveTransactions.Add(tx);
                }
                finally
                {
                    _txCommit.ExitReadLock();
                }

                var state = _dataPager.PagerState;
                tx.EnsurePagerStateReference(state);

                return(tx);
            }
            catch (Exception)
            {
                if (txLockTaken)
                {
                    Monitor.Exit(_txWriter);
                }
                if (flushInProgressReadLockTaken)
                {
                    FlushInProgressLock.ExitReadLock();
                }
                throw;
            }
        }