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();
            }
        }