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 }); } }
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); } }
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; } }
public Transaction WriteTransaction(TransactionPersistentContext transactionPersistentContext, ByteStringContext context = null) { var writeTransaction = new Transaction(NewLowLevelTransaction(transactionPersistentContext, TransactionFlags.ReadWrite, context, null)); return(writeTransaction); }
public Transaction ReadTransaction(TransactionPersistentContext transactionPersistentContext, ByteStringContext context = null) { return(new Transaction(NewLowLevelTransaction(transactionPersistentContext, TransactionFlags.Read, context))); }
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(); } } } }
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; } }