Example #1
0
        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);
        }
Example #3
0
        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));
        }
Example #6
0
        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;
		}