private List <OutstandingWrite> BuildBatchGroup(OutstandingWrite mine) { // Allow the group to grow up to a maximum size, but if the // original write is small, limit the growth so we do not slow // down the small write too much. long maxSize = 1024 * 1024; // 1 MB by default if (mine.Size < 128 * 1024) { maxSize = mine.Size + (128 * 1024); } var list = new List <OutstandingWrite> { mine }; foreach (var write in _pendingWrites) { if (maxSize <= 0) { break; } if (write == mine) { continue; } list.Add(write); maxSize -= write.Size; } return(list); }
private List <OutstandingWrite> BuildBatchGroup(OutstandingWrite mine) { // Allow the group to grow up to a maximum size, but if the // original write is small, limit the growth so we do not slow // down the small write too much. long maxSize = 64 * 1024 * 1024; // 64 MB by default if (mine.Size < 128 * 1024) { maxSize = (2 * 1024 * 1024); // 2 MB if small } var list = new List <OutstandingWrite> { mine }; maxSize -= mine.Size; while (true) { if (maxSize <= 0) { break; } OutstandingWrite item; if (_pendingWrites.TryDequeue(out item) == false) { break; } list.Add(item); maxSize -= item.Size; } return(list); }
public async Task WriteAsync(WriteBatch batch) { var mine = new OutstandingWrite(batch); _pendingWrites.Enqueue(mine); List <OutstandingWrite> writes = null; await _semaphore.WaitAsync(); try { if (mine.Done) { return; } writes = BuildBatchGroup(mine); using (var tx = _env.NewTransaction(TransactionFlags.ReadWrite)) { foreach (var g in writes.SelectMany(x => x.Batch.Operations).GroupBy(x => x.TreeName)) { var tree = GetTree(g.Key); foreach (var operation in g) { switch (operation.Type) { case WriteBatch.BatchOperationType.Add: tree.Add(tx, operation.Key, operation.Value); break; case WriteBatch.BatchOperationType.Delete: tree.Delete(tx, operation.Key); break; } } } tx.Commit(); } } finally { if (writes != null) { foreach (var write in writes) { Debug.Assert(_pendingWrites.Peek() == write); OutstandingWrite pendingWrite; _pendingWrites.TryDequeue(out pendingWrite); pendingWrite.Done = true; } } _semaphore.Release(); } }
private IEnumerable<WriteBatch.BatchOperation> GetBatchOperations(OutstandingWrite write) { var trees = write.Trees.ToList(); var operations = new List<WriteBatch.BatchOperation>(); trees.ForEach(tree => operations.AddRange(write.GetOperations(tree))); return operations.Where(x => x != null); }
private IEnumerable <WriteBatch.BatchOperation> GetBatchOperations(OutstandingWrite write) { var trees = write.Trees.ToList(); var operations = new List <WriteBatch.BatchOperation>(); trees.ForEach(tree => operations.AddRange(write.GetOperations(tree))); return(operations.Where(x => x != null)); }
private void HandleActualWrites(OutstandingWrite mine, CancellationToken token) { List <OutstandingWrite> writes = null; try { writes = BuildBatchGroup(mine); var completedSuccessfully = false; using (var tx = _env.NewTransaction(TransactionFlags.ReadWrite)) { HandleOperations(tx, writes, _cancellationToken); try { tx.Commit(); if (ShouldRecordToDebugJournal) { _debugJournal.Flush(); } completedSuccessfully = true; } catch (Exception e) { if (e is SEHException) { e = new VoronUnrecoverableErrorException("Error occurred during write", new Win32Exception(error: e.HResult)); } // if we have an error during the commit, we can't recover, just fail them all. foreach (var write in writes) { write.Errored(e); } } } if (completedSuccessfully) { foreach (var write in writes) { write.Completed(); } } } catch (Exception e) { if (e is SEHException) { e = new VoronUnrecoverableErrorException("Error occurred during write", new Win32Exception(error: e.HResult)); } HandleWriteFailure(writes, mine, e); } }
public void Write(WriteBatch batch) { if (batch.IsEmpty) return; EnsureValidBackgroundTaskState(); using (var mine = new OutstandingWrite(batch, this)) { _pendingWrites.Enqueue(mine); _hasWrites.Set(); mine.Wait(); } }
private void HandleWriteFailure(List <OutstandingWrite> writes, OutstandingWrite mine, Exception e) { if (writes == null || writes.Count == 0) { mine.Errored(e); throw new InvalidOperationException("Couldn't get items to write", e); } if (writes.Count == 1) { writes[0].Errored(e); return; } SplitWrites(writes); }
public void Write(WriteBatch batch) { if (batch.IsEmpty) { return; } EnsureValidBackgroundTaskState(); using (var mine = new OutstandingWrite(batch, this)) { _pendingWrites.Enqueue(mine); _hasWrites.Set(); mine.Wait(); } }
private void HandleActualWrites(OutstandingWrite mine, CancellationToken token) { List <OutstandingWrite> writes = null; try { writes = BuildBatchGroup(mine); using (var tx = _env.NewTransaction(TransactionFlags.ReadWrite)) { HandleOperations(tx, writes, _cancellationToken); try { tx.Commit(); if (ShouldRecordToDebugJournal) { _debugJournal.Flush(); } foreach (var write in writes) { write.Completed(); } } catch (Exception e) { // if we have an error duing the commit, we can't recover, just fail them all. foreach (var write in writes) { write.Errored(e); } } } } catch (Exception e) { HandleWriteFailure(writes, mine, e); } }
private List<OutstandingWrite> BuildBatchGroup(OutstandingWrite mine) { // Allow the group to grow up to a maximum size, but if the // original write is small, limit the growth so we do not slow // down the small write too much. long maxSize = 64 * 1024 * 1024; // 64 MB by default if (mine.Size < 128 * 1024) maxSize = (2 * 1024 * 1024); // 2 MB if small var list = new List<OutstandingWrite> { mine }; maxSize -= mine.Size; while (true) { if (maxSize <= 0) break; OutstandingWrite item; if (_pendingWrites.TryDequeue(out item) == false) break; list.Add(item); maxSize -= item.Size; } return list; }
private void HandleWriteFailure(List<OutstandingWrite> writes, OutstandingWrite mine, Exception e) { if (writes == null || writes.Count == 0) { mine.Errored(e); throw new InvalidOperationException("Couldn't get items to write", e); } if (writes.Count == 1) { writes[0].Errored(e); return; } SplitWrites(writes); }
private void HandleActualWrites(OutstandingWrite mine, CancellationToken token) { List<OutstandingWrite> writes = null; try { writes = BuildBatchGroup(mine); var completedSuccessfully = false; using (var tx = _env.NewTransaction(TransactionFlags.ReadWrite)) { HandleOperations(tx, writes, _cancellationToken); try { tx.Commit(); if (ShouldRecordToDebugJournal) _debugJournal.Flush(); completedSuccessfully = true; } catch (Exception e) { if (e is SEHException) { e = new VoronUnrecoverableErrorException("Error occurred during write", new Win32Exception(error: e.HResult)); } // if we have an error during the commit, we can't recover, just fail them all. foreach (var write in writes) { write.Errored(e); } } } if (completedSuccessfully) { foreach (var write in writes) write.Completed(); } } catch (Exception e) { if (e is SEHException) { e = new VoronUnrecoverableErrorException("Error occurred during write", new Win32Exception(error: e.HResult)); } HandleWriteFailure(writes, mine, e); } }
private void HandleActualWrites(OutstandingWrite mine, CancellationToken token) { List<OutstandingWrite> writes = null; try { writes = BuildBatchGroup(mine); using (var tx = _env.NewTransaction(TransactionFlags.ReadWrite)) { HandleOperations(tx, writes, _cancellationToken); try { tx.Commit(); if (ShouldRecordToDebugJournal) _debugJournal.Flush(); foreach (var write in writes) write.Completed(); } catch (Exception e) { // if we have an error duing the commit, we can't recover, just fail them all. foreach (var write in writes) { write.Errored(e); } } } } catch (Exception e) { HandleWriteFailure(writes, mine, e); } }
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(); } }
private List<OutstandingWrite> BuildBatchGroup(OutstandingWrite mine) { // Allow the group to grow up to a maximum size, but if the // original write is small, limit the growth so we do not slow // down the small write too much. long maxSize = 1024 * 1024; // 1 MB by default if (mine.Size < 128 * 1024) maxSize = mine.Size + (128 * 1024); var list = new List<OutstandingWrite> { mine }; foreach (OutstandingWrite item in _pendingWrites) { if (maxSize <= 0) break; if (item == mine) continue; if (item.Options.FlushToDisk != mine.Options.FlushToDisk) break; // we can only take items that have the same flush to disk behavior list.Add(item); maxSize -= item.Size; } return list; }