private void Read(Status status, int readerIdx, CommandProcessorContext context, ManualResetEventSlim finishedEvent)
        {
            TcpTypedConnection<byte[]> connection;
            var iteration = new AutoResetEvent(false);

            var successes = 0;
            var fails = 0;

            var rnd = new Random();

            var streamIdx = -1;
            var eventidx = -1;

            Action<TcpTypedConnection<byte[]>, TcpPackage> packageReceived = (conn, pkg) =>
            {
                var dto = pkg.Data.Deserialize<TcpClientMessageDto.ReadEventCompleted>();
                switch ((ReadEventResult)dto.Result)
                {
                    case ReadEventResult.Success:
                        if (Equal(_streams[streamIdx], eventidx, dto.Event.Event.EventType, dto.Event.Event.Data))
                        {
                            successes++;
                            if (successes % 1000 == 0)
                                status.ReportReadsProgress(readerIdx, successes, fails);
                        }
                        else
                        {
                            fails++;
                            status.ReportReadError(readerIdx, _streams[streamIdx], eventidx);
                        }
                        break;
                    case ReadEventResult.NotFound:
                    case ReadEventResult.NoStream:
                    case ReadEventResult.StreamDeleted:
                    case ReadEventResult.Error:
                    case ReadEventResult.AccessDenied:
                        fails++;
                        status.ReportNotFoundOnRead(readerIdx, _streams[streamIdx], eventidx);
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }

                iteration.Set();
            };
            Action<TcpTypedConnection<byte[]>> established = _ => { };
            Action<TcpTypedConnection<byte[]>, SocketError> closed = null;
            closed = (_, __) =>
            {
                Thread.Sleep(TimeSpan.FromSeconds(1));
                connection = context.Client.CreateTcpConnection(context, packageReceived, cn => iteration.Set(), closed, false);
            };

            connection = context.Client.CreateTcpConnection(context, packageReceived, established, closed, false);

            while (!_stopReading)
            {
                streamIdx = NextStreamForReading(rnd, readerIdx);
                int head;
                lock (_heads)
                    head = _heads[streamIdx];

                if (head > 0)
                {
                    eventidx = NextRandomEventVersion(rnd, head);
                    var stream = _streams[streamIdx];
                    var corrid = Guid.NewGuid();
                    var read = new TcpClientMessageDto.ReadEvent(stream, eventidx, resolveLinkTos: false, requireMaster: false);
                    var package = new TcpPackage(TcpCommand.ReadEvent, corrid, read.Serialize());

                    connection.EnqueueSend(package.AsByteArray());
                    iteration.WaitOne();
                }
                else
                    Thread.Sleep(100);
            }

            status.ReportReadsProgress(readerIdx, successes, fails);
            status.FinilizeStatus(readerIdx, fails == 0);
            connection.Close();
            finishedEvent.Set();
        }
        private void Write(Status status, int writerIdx, CommandProcessorContext context, int requests, ManualResetEventSlim finish)
        {
            TcpTypedConnection<byte[]> connection;
            var iteration = new AutoResetEvent(false);

            var sent = 0;

            var prepareTimeouts = 0;
            var commitTimeouts = 0;
            var forwardTimeouts = 0;
            var wrongExpectedVersion = 0;
            var streamsDeleted = 0;

            var failed = 0;

            var rnd = new Random();

            var streamIdx = -1;
            var head = -1;

            Action<TcpTypedConnection<byte[]>, TcpPackage> packageHandler = (conn, pkg) =>
            {
                var dto = pkg.Data.Deserialize<TcpClientMessageDto.WriteEventsCompleted>();
                switch (dto.Result)
                {
                    case TcpClientMessageDto.OperationResult.Success:
                        lock (_heads)
                        {
                            var currentHead = _heads[streamIdx];
                            Ensure.Equal(currentHead, head, "currentHead");
                            _heads[streamIdx]++;
                        }
                        break;
                    case TcpClientMessageDto.OperationResult.PrepareTimeout:
                        prepareTimeouts++;
                        failed++;
                        break;
                    case TcpClientMessageDto.OperationResult.CommitTimeout:
                        commitTimeouts++;
                        failed++;
                        break;
                    case TcpClientMessageDto.OperationResult.ForwardTimeout:
                        forwardTimeouts++;
                        failed++;
                        break;
                    case TcpClientMessageDto.OperationResult.WrongExpectedVersion:
                        wrongExpectedVersion++;
                        failed++;
                        break;
                    case TcpClientMessageDto.OperationResult.StreamDeleted:
                        streamsDeleted++;
                        failed++;
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }

                sent++;
                if (sent % 1000 == 0)
                    status.ReportWritesProgress(writerIdx, sent, prepareTimeouts, commitTimeouts, forwardTimeouts,
                                                wrongExpectedVersion, streamsDeleted, failed, requests);
                if (sent == requests)
                    finish.Set();

                iteration.Set();
            };

            Action<TcpTypedConnection<byte[]>> established = _ => { };
            Action<TcpTypedConnection<byte[]>, SocketError> closed = null;
            closed = (_, __) =>
            {
                Thread.Sleep(TimeSpan.FromSeconds(1));
                connection = context.Client.CreateTcpConnection(context, packageHandler, cn => iteration.Set(), closed, false);
            };

            connection = context.Client.CreateTcpConnection(context, packageHandler, established, closed, false);

            for (var i = 0; i < requests; ++i)
            {
                streamIdx = NextStreamForWriting(rnd, writerIdx);
                lock (_heads)
                {
                    head = _heads[streamIdx];
                }
                var evnt = CreateEvent(_streams[streamIdx], head + 1);
                var write = new TcpClientMessageDto.WriteEvents(
                    _streams[streamIdx],
                    head,
                    new[] 
                    { 
                        new TcpClientMessageDto.NewEvent(evnt.EventId.ToByteArray(), evnt.EventType,evnt.IsJson ? 1 : 0, 0, evnt.Data, evnt.Metadata) 
                    },
                    false);

                var package = new TcpPackage(TcpCommand.WriteEvents, Guid.NewGuid(), write.Serialize());
                connection.EnqueueSend(package.AsByteArray());
                iteration.WaitOne();
            }

            status.ReportWritesProgress(writerIdx, sent, prepareTimeouts, commitTimeouts, forwardTimeouts,
                                        wrongExpectedVersion, streamsDeleted, failed, requests);
            status.FinilizeStatus(writerIdx, failed != sent);
            connection.Close();
        }