/// <summary> /// Rolls back transaction by copying all backed up data bask to master stream and closes the transaction. /// </summary> public void RollbackTransaction() { if (logStreamHeader != null) { if (logStreamHeader.TransactionCompleted) { throw new InvalidOperationException("Can't rollback completed transaction"); } logStream.Position = TransactionLogStreamMetadata.StructureSize; // Copy segments from log stream back to stream for (int i = 0; i < logStreamHeader.SegmentCount; i++) { SegmentMetadata sh = SegmentMetadata.Load(logStream); if (!sh.TransactionId.Equals(logStreamHeader.TransactionId)) { throw new InvalidOperationException("Wrong segment found in transaction log"); } masterStream.Position = sh.Position; CopyData(logStream, sh.Size, masterStream); } // Set back original stream length if (logStreamHeader.StreamLength < masterStream.Length) { masterStream.SetLength(logStreamHeader.StreamLength); } masterStream.Flush(); logStream.SetLength(0); logStream.Flush(); logStreamHeader = null; rollbackNeeded = false; } }
/// <summary> /// Loads the metadata from the beginning of the stream /// </summary> public static TransactionLogStreamMetadata Load(Stream stream) { BinaryReader reader = new BinaryReader(stream); TransactionLogStreamMetadata lsh = new TransactionLogStreamMetadata { TransactionId = new Guid(reader.ReadBytes(16)), StreamLength = reader.ReadInt64(), SegmentCount = reader.ReadInt64(), TransactionCompleted = reader.ReadBoolean() }; return lsh; }
/// <summary> /// Loads the metadata from the beginning of the stream /// </summary> public static TransactionLogStreamMetadata Load(Stream stream) { BinaryReader reader = new BinaryReader(stream); TransactionLogStreamMetadata lsh = new TransactionLogStreamMetadata { TransactionId = new Guid(reader.ReadBytes(16)), StreamLength = reader.ReadInt64(), SegmentCount = reader.ReadInt64(), TransactionCompleted = reader.ReadBoolean() }; return(lsh); }
/// <summary> /// Consrtructs a transaction stream on top of the specified master stream /// </summary> /// <param name="masterStream">Stream for which backup si performed</param> /// <param name="logStream">Stream where backed up data is stored</param> /// <param name="blockSize">Size of the minimum block size that will be backed up</param> public TransactionStream(Stream masterStream, Stream logStream, long blockSize) { this.masterStream = masterStream; this.logStream = logStream; this.blockSize = blockSize; // Check if previous transaction has completed if (logStream != null && logStream.Length > TransactionLogStreamMetadata.StructureSize) { logStreamHeader = TransactionLogStreamMetadata.Load(logStream); rollbackNeeded = !logStreamHeader.TransactionCompleted; if (logStreamHeader.SegmentCount == 0 || logStreamHeader.TransactionCompleted) logStreamHeader = null; } }
/// <summary> /// Marks in the log file that transaction has ended /// </summary> public void EndTransaction() { CheckState(); if (logStreamHeader != null) { masterStream.Flush(); logStreamHeader.TransactionCompleted = true; logStreamHeader.Save(logStream); logStream.Flush(); logStreamHeader = null; CheckSegments(); } }
/// <summary> /// Consrtructs a transaction stream on top of the specified master stream /// </summary> /// <param name="masterStream">Stream for which backup si performed</param> /// <param name="logStream">Stream where backed up data is stored</param> /// <param name="blockSize">Size of the minimum block size that will be backed up</param> public TransactionStream(Stream masterStream, Stream logStream, long blockSize) { this.masterStream = masterStream; this.logStream = logStream; this.blockSize = blockSize; // Check if previous transaction has completed if (logStream != null && logStream.Length > TransactionLogStreamMetadata.StructureSize) { logStreamHeader = TransactionLogStreamMetadata.Load(logStream); rollbackNeeded = !logStreamHeader.TransactionCompleted; if (logStreamHeader.SegmentCount == 0 || logStreamHeader.TransactionCompleted) { logStreamHeader = null; } } }
/// <summary> /// Start transaction /// </summary> /// <param name="initialSegments">Areas in master stream which don't require backing up. Can be null.</param> public Guid BeginTransaction(IEnumerable <Segment> initialSegments = null) { CheckState(); if (logStream == null) { throw new InvalidOperationException("Log stream not specified"); } if (logStreamHeader != null) { throw new InvalidOperationException("Unable to start new transaction while existing transaction is in progress"); } // Add custom segments that will not be backed up segments.Clear(); if (initialSegments != null) { IOrderedEnumerable <Segment> orderedList = initialSegments.OrderBy(x => x.Location); foreach (var segment in orderedList) { segments.AddLast(segment); } } // Add unallocated space after the end of stream segments.AddLast(new Segment(masterStream.Length, long.MaxValue - masterStream.Length)); CheckSegments(); // Truncate transaction log logStream.SetLength(0); logStream.Flush(); // Initialize transaction log logStreamHeader = new TransactionLogStreamMetadata { TransactionId = Guid.NewGuid(), StreamLength = masterStream.Length, SegmentCount = 0, TransactionCompleted = false }; logStreamHeader.Save(logStream); logStream.Flush(); return(logStreamHeader.TransactionId); }
/// <summary> /// Rolls back transaction by copying all backed up data bask to master stream and closes the transaction. /// </summary> public void RollbackTransaction() { if (logStreamHeader != null) { if (logStreamHeader.TransactionCompleted) throw new InvalidOperationException("Can't rollback completed transaction"); logStream.Position = TransactionLogStreamMetadata.StructureSize; // Copy segments from log stream back to stream for (int i = 0; i < logStreamHeader.SegmentCount; i++) { SegmentMetadata sh = SegmentMetadata.Load(logStream); if (!sh.TransactionId.Equals(logStreamHeader.TransactionId)) throw new InvalidOperationException("Wrong segment found in transaction log"); masterStream.Position = sh.Position; CopyData(logStream, sh.Size, masterStream); } // Set back original stream length if (logStreamHeader.StreamLength < masterStream.Length) masterStream.SetLength(logStreamHeader.StreamLength); masterStream.Flush(); logStream.SetLength(0); logStream.Flush(); logStreamHeader = null; rollbackNeeded = false; } }
/// <summary> /// Marks in the log file that transaction has ended /// </summary> public void EndTransaction() { CheckState(); if (logStreamHeader != null) { masterStream.Flush(); logStreamHeader.TransactionCompleted = true; logStreamHeader.Save(logStream); logStream.Flush(); logStreamHeader = null; CheckSegments(); } }
/// <summary> /// Start transaction /// </summary> /// <param name="initialSegments">Areas in master stream which don't require backing up. Can be null.</param> public Guid BeginTransaction(IEnumerable<Segment> initialSegments = null) { CheckState(); if (logStream == null) throw new InvalidOperationException("Log stream not specified"); if (logStreamHeader != null) throw new InvalidOperationException("Unable to start new transaction while existing transaction is in progress"); // Add custom segments that will not be backed up segments.Clear(); if (initialSegments != null) { IOrderedEnumerable<Segment> orderedList = initialSegments.OrderBy(x => x.Location); foreach (var segment in orderedList) { segments.AddLast(segment); } } // Add unallocated space after the end of stream segments.AddLast(new Segment(masterStream.Length, long.MaxValue - masterStream.Length)); CheckSegments(); // Truncate transaction log logStream.SetLength(0); logStream.Flush(); // Initialize transaction log logStreamHeader = new TransactionLogStreamMetadata { TransactionId = Guid.NewGuid(), StreamLength = masterStream.Length, SegmentCount = 0, TransactionCompleted = false }; logStreamHeader.Save(logStream); logStream.Flush(); return logStreamHeader.TransactionId; }