public void CommitTransaction(ICollection <PersistentQueueOperation> operations) { if (operations.Count == 0) { return; } byte[] transactionBuffer = GenerateTransactionBuffer(operations); lock (_transactionLogLock) { long txLogSize; using (FileStream stream = WaitForTransactionLog(transactionBuffer)) { stream.Write(transactionBuffer, 0, transactionBuffer.Length); txLogSize = stream.Position; stream.Flush(); } ApplyTransactionOperations(operations); TrimTransactionLogIfNeeded(txLogSize); PersistentQueueUtils.Write(Meta, stream => { byte[] bytes = BitConverter.GetBytes(CurrentFileNumber); stream.Write(bytes, 0, bytes.Length); bytes = BitConverter.GetBytes(CurrentFilePosition); stream.Write(bytes, 0, bytes.Length); }); FlushTrimmedTransactionLog(); } }
private void ReadMetaState() { PersistentQueueUtils.Read(Meta, stream => { using (BinaryReader binaryReader = new BinaryReader(stream)) { try { CurrentFileNumber = binaryReader.ReadInt32(); CurrentFilePosition = binaryReader.ReadInt64(); } catch (EndOfStreamException) { } } }); }
private void FlushTrimmedTransactionLog() { byte[] transactionBuffer; using (MemoryStream ms = new MemoryStream()) { ms.Write(PersistentQueueUtils.StartTransactionSeparator, 0, PersistentQueueUtils.StartTransactionSeparator.Length); byte[] count = BitConverter.GetBytes(CurrentCountOfItemsInQueue); ms.Write(count, 0, count.Length); PersistentQueueEntry[] checkedOut; lock (_checkedOutEntries) { checkedOut = _checkedOutEntries.ToArray(); } foreach (var entry in checkedOut) { WriteEntryToTransactionLog(ms, entry, PersistentQueueOperationTypes.ENQUEUE); } PersistentQueueEntry[] listedEntries; lock (_entries) { listedEntries = _entries.ToArray(); } foreach (var entry in listedEntries) { // TODO Use Jobs here WriteEntryToTransactionLog(ms, entry, PersistentQueueOperationTypes.ENQUEUE); } ms.Write(PersistentQueueUtils.EndTransactionSeparator, 0, PersistentQueueUtils.EndTransactionSeparator.Length); ms.Flush(); transactionBuffer = ms.ToArray(); } PersistentQueueUtils.Write(TransactionLog, stream => { stream.SetLength(transactionBuffer.Length); stream.Write(transactionBuffer, 0, transactionBuffer.Length); }); }
private void ReadTransactionLog() { var requireTxLogTrimming = false; PersistentQueueUtils.Read(TransactionLog, stream => { using (var binaryReader = new BinaryReader(stream)) { bool readingTransaction = false; try { int txCount = 0; while (true) { txCount += 1; // this code ensures that we read the full transaction // before we start to apply it. The last truncated transaction will be // ignored automatically. AssertTransactionSeparator(binaryReader, txCount, PersistentQueueMarkers.START, () => readingTransaction = true); var opsCount = binaryReader.ReadInt32(); var txOps = new List <PersistentQueueOperation>(opsCount); for (var i = 0; i < opsCount; i++) { AssertOperationSeparator(binaryReader); var operation = new PersistentQueueOperation( (PersistentQueueOperationTypes)binaryReader.ReadByte(), binaryReader.ReadInt32(), binaryReader.ReadInt32(), binaryReader.ReadInt32() ); txOps.Add(operation); //if we have non enqueue entries, this means // that we have not closed properly, so we need // to trim the log if (operation.Type != PersistentQueueOperationTypes.ENQUEUE) { requireTxLogTrimming = true; } } // check that the end marker is in place AssertTransactionSeparator(binaryReader, txCount, PersistentQueueMarkers.END, () => { }); readingTransaction = false; ApplyTransactionOperations(txOps); } } catch (EndOfStreamException) { // we have a truncated transaction, need to clear that if (readingTransaction) { requireTxLogTrimming = true; } } } }); if (requireTxLogTrimming) { FlushTrimmedTransactionLog(); } }