/// <summary> /// Start a transaction /// </summary> public void StartTransaction() { try { CheckClosed(); transactionLevel++; if (transactionLevel == 1) { if (streamsChangedDuringTransaction.Count > 0) { throw new StorageException("At the begining of transaction there should be no changed streams"); } NotifyTransactionChanging(TransactionStateChangeType.Start); MasterStream.StartTransaction(); if (transactionStream != null) { // Make a list of extents that doesn't need to be backed up IEnumerable <Transactions.Segment> list = FreeSpaceStream != null?FreeSpaceStream.Segments.Select(x => new Transactions.Segment(x.DataAreaStart, x.DataAreaSize)) : null; transactionStream.BeginTransaction(list); } NotifyTransactionChanged(TransactionStateChangeType.Start); } } catch { InternalRollbackTransaction(); throw; } }
/// <summary> /// Closes the storage /// </summary> public void Close() { if (transactionLevel > 0) { InternalRollbackTransaction(); throw new StorageException("Unable to close storage while transaction is pending"); } if (!isClosed) { lock (openedStreams) { //cacheCleanupTimer.Dispose(); //cacheCleanupTimer = null; RollbackTransaction(); // Cache stream table into empty space stream MasterStream.Flush(); MasterStream.Close(); openedStreams.Clear(); streamsChangedDuringTransaction.Clear(); isClosed = true; } } }
private void InternalRollbackTransaction() { if (transactionLevel > 0) { // Remove opened streams created during transaction lock (openedStreams) { foreach (Guid streamId in streamsCreatedDuringTransaction) { WeakReference <StorageStream> reference; if (openedStreams.TryGetValue(streamId, out reference)) { StorageStream tmpStream; if (reference.TryGetTarget(out tmpStream)) { if (tmpStream != null) { tmpStream.InternalClose(); } } openedStreams.Remove(streamId); } } streamsCreatedDuringTransaction.Clear(); // Rollback data transactionStream.RollbackTransaction(); MasterStream.RollbackTransaction(); streamsChangedDuringTransaction.Clear(); // Rollback changes in stream table streamTableStream.ReloadSegmentsOnRollback(streamTableStreamMetadata); streamTable.RollbackTransaction(); // Reload segments in system and opened streams because segments has changed foreach (var item in openedStreams.Values.ToList()) { StorageStream tmpStream; if (item.TryGetTarget(out tmpStream)) { if (streamTable.Contains(tmpStream.StreamId)) { StorageStreamMetadata tmpStreamMetadata = streamTable.Get(tmpStream.StreamId); tmpStream.ReloadSegmentsOnRollback(tmpStreamMetadata); } else { tmpStream.InternalClose(); } } } // Reload empty space segments var freeSpaceStreamMetadata = streamTable.Get(SystemStreamId.EmptySpace); FreeSpaceStream.ReloadSegmentsOnRollback(freeSpaceStreamMetadata); } } }
/// <summary> /// Trim the master file to the location where data ends /// </summary> public void TrimStorage() { Segment lastSegment = FreeSpaceStream.Segments.SingleOrDefault(x => !x.NextLocation.HasValue); if (lastSegment != null) { MasterStream.SetLength(lastSegment.DataAreaStart); } }
/// <summary> /// Commits a transaction /// </summary> public void CommitTransaction() { try { CheckClosed(); if (transactionLevel == 1) { NotifyTransactionChanging(TransactionStateChangeType.Commit); SaveChanges(); if (transactionStream != null) { transactionStream.EndTransaction(); } streamsCreatedDuringTransaction.Clear(); MasterStream.Flush(); MasterStream.CommitTransaction(); Statistics.TransactionsCommited++; } if (transactionLevel > 0) { transactionLevel--; if (transactionLevel == 0) { NotifyTransactionChanged(TransactionStateChangeType.Commit); } } } catch { InternalRollbackTransaction(); throw; } }