public async Task WriteAsync(WriteBatch batch, WriteOptions options = null) { if (Log.IsDebugEnabled) Log.Debug(batch.DebugVal); if (options == null) options = new WriteOptions(); var mine = new OutstandingWrite(batch, options); _pendingWrites.Enqueue(mine); List<OutstandingWrite> list = null; await semaphore.WaitAsync(); try { if (mine.Done) { if (Log.IsDebugEnabled) Log.Debug("Write batch #{0} was completed early, done (no lock needed).", batch.BatchId); return; } using (AsyncLock.LockScope locker = await _state.Lock.LockAsync().ConfigureAwait(false)) { await _state.MakeRoomForWriteAsync(force: false, lockScope: locker).ConfigureAwait(false); ulong lastSequence = _state.VersionSet.LastSequence; list = BuildBatchGroup(mine); if (list.Count > 1) { if (Log.IsDebugEnabled) Log.Debug("Write batch #{0} will be written along with {1} batches, all at once.", batch.BatchId, list.Count); } ulong currentSequence = lastSequence + 1; var currentSequenceRef = new Reference<ulong> { Value = lastSequence + 1 }; lastSequence += (ulong)list.Sum(x => x.Batch.OperationCount); // Add to log and apply to memtable. We can release the lock // during this phase since mine is currently responsible for logging // and protects against concurrent loggers and concurrent writes // into the mem table. locker.Exit(); { list.ForEach(write => write.Batch.Prepare(_state.MemTable)); try { await Task.WhenAll( WriteBatch.WriteToLogAsync(list.Select(x => x.Batch).ToArray(), currentSequence, _state, options), Task.Run(() => list.ForEach(write => write.Batch.Apply(_state.MemTable, currentSequenceRef)))); } catch (LogWriterException e) { Log.ErrorException("Writing to log failed.", e); currentSequenceRef = new Reference<ulong> { Value = lastSequence + 1 }; list.ForEach(write => write.Batch.Remove(_state.MemTable, currentSequenceRef)); throw; } } await locker.LockAsync().ConfigureAwait(false); _state.VersionSet.LastSequence = lastSequence; } } finally { if (list != null) { int count = 0; long size = 0; foreach (OutstandingWrite item in list) { Debug.Assert(_pendingWrites.Peek() == item); OutstandingWrite write; _pendingWrites.TryDequeue(out write); count += write.Batch.OperationCount; size += write.Size; write.Done = true; } _state.PerfCounters.Write(count); _state.PerfCounters.BytesWritten(size); } semaphore.Release(); } }
internal static Task WriteToLogAsync(WriteBatch[] writes, ulong seq, StorageState state, WriteOptions options) { return Task.Factory.StartNew( () => { try { var opCount = writes.Sum(x => x._operations.Count); if (log.IsDebugEnabled) log.Debug("Writing {0} operations in seq {1}", opCount, seq); state.LogWriter.RecordStarted(); var buffer = new byte[12]; Bit.Set(buffer, 0, seq); Bit.Set(buffer, 8, opCount); state.LogWriter.Write(buffer, 0, 12); foreach (var operation in writes.SelectMany(writeBatch => writeBatch._operations)) { buffer[0] = (byte)operation.Op; state.LogWriter.Write(buffer, 0, 1); state.LogWriter.Write7BitEncodedInt(operation.Key.Count); state.LogWriter.Write(operation.Key.Array, operation.Key.Offset, operation.Key.Count); if (operation.Op != Operations.Put) continue; Bit.Set(buffer, 0, operation.Handle.Size); state.LogWriter.Write(buffer, 0, 4); using (var stream = state.MemTable.Read(operation.Handle)) { state.LogWriter.CopyFrom(stream); } } state.LogWriter.RecordCompleted(options.FlushToDisk); if (log.IsDebugEnabled) log.Debug("Wrote {0} operations in seq {1} to log.", opCount, seq); } catch (Exception e) { state.LogWriter.ResetToLastCompletedRecord(); throw new LogWriterException(e); } }); }
public OutstandingWrite(WriteBatch batch, WriteOptions options) { Batch = batch; Options = options; Size = batch.Size; }