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