private static TcpPackage WrapWriteEvents(ClientMessage.WriteEvents msg)
        {
            var dto = new ClientMessageDto.WriteEvents(
                msg.CorrelationId,
                msg.EventStreamId,
                msg.ExpectedVersion,
                msg.Events.Select(x => new ClientMessageDto.Event(x.EventId, x.EventType, x.Data, x.Metadata)).ToArray());

            return(new TcpPackage(TcpCommand.WriteEvents, msg.CorrelationId, dto.Serialize()));
        }
Beispiel #2
0
        public Task <WriteResult> AppendToStreamAsync(string stream, int expectedVersion, IEnumerable <Event> events)
        {
            var correlationId = Guid.NewGuid();

            var eventDtos = events.Select(x => new ClientMessageDto.Event(x.EventId,
                                                                          x.Type,
                                                                          x.Data,
                                                                          x.Metadata)).ToArray();

            var dto = new ClientMessageDto.WriteEvents(correlationId,
                                                       stream,
                                                       expectedVersion,
                                                       eventDtos);

            var package = new TcpPackage(TcpCommand.WriteEvents, correlationId, dto.Serialize());
            var taskCompletionSource = new TaskCompletionSource <WriteResult>();
            var taskWrapper          = new WriteTaskCompletionWrapper(taskCompletionSource);

            RegisterHandler(correlationId, taskWrapper);
            EnqueueForSend(package);

            return(taskCompletionSource.Task);
        }
Beispiel #3
0
        public override TcpPackage CreateNetworkPackage(Guid correlationId)
        {
            lock (_stepMoveLock)
            {
                switch (_step)
                {
                case 0:
                    var writeDto = new ClientMessageDto.WriteEvents(correlationId,
                                                                    _event.EventStreamId,
                                                                    _event.ExpectedVersion,
                                                                    new[] { new ClientMessageDto.Event(_event.Event) });
                    return(new TcpPackage(TcpCommand.WriteEvents, correlationId, writeDto.Serialize()));

                case 1:
                    var readDto = new ClientMessageDto.ReadEvent(correlationId,
                                                                 _event.EventStreamId,
                                                                 _event.ShouldBeVersion);
                    return(new TcpPackage(TcpCommand.ReadEvent, correlationId, readDto.Serialize()));

                default:
                    throw new ArgumentOutOfRangeException("_step", "step can be 0 or 1 for write task");
                }
            }
        }
        private void WriteFlood(CommandProcessorContext context, int clientsCnt, long requestsCnt, int streamsCnt)
        {
            context.IsAsync();

            var doneEvent = new AutoResetEvent(false);
            var clients   = new List <TcpTypedConnection <byte[]> >();
            var threads   = new List <Thread>();

            long succ            = 0;
            long fail            = 0;
            long prepTimeout     = 0;
            long commitTimeout   = 0;
            long forwardTimeout  = 0;
            long wrongExpVersion = 0;
            long streamDeleted   = 0;
            long all             = 0;

            var streams = Enumerable.Range(0, streamsCnt).Select(x => Guid.NewGuid().ToString()).ToArray();
            var sw2     = new Stopwatch();

            for (int i = 0; i < clientsCnt; i++)
            {
                var count = requestsCnt / clientsCnt + ((i == clientsCnt - 1) ? requestsCnt % clientsCnt : 0);

                int sent     = 0;
                int received = 0;
                var rnd      = new Random();

                var client = context.Client.CreateTcpConnection(
                    context,
                    (conn, pkg) =>
                {
                    if (pkg.Command != TcpCommand.WriteEventsCompleted)
                    {
                        context.Fail(reason: string.Format("Unexpected TCP package: {0}.", pkg.Command));
                        return;
                    }

                    var dto = pkg.Data.Deserialize <ClientMessageDto.WriteEventsCompleted>();
                    switch ((OperationErrorCode)dto.ErrorCode)
                    {
                    case OperationErrorCode.Success:
                        Interlocked.Increment(ref succ);
                        break;

                    case OperationErrorCode.PrepareTimeout:
                        Interlocked.Increment(ref prepTimeout);
                        break;

                    case OperationErrorCode.CommitTimeout:
                        Interlocked.Increment(ref commitTimeout);
                        break;

                    case OperationErrorCode.ForwardTimeout:
                        Interlocked.Increment(ref forwardTimeout);
                        break;

                    case OperationErrorCode.WrongExpectedVersion:
                        Interlocked.Increment(ref wrongExpVersion);
                        break;

                    case OperationErrorCode.StreamDeleted:
                        Interlocked.Increment(ref streamDeleted);
                        break;

                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                    if (dto.ErrorCode != (int)OperationErrorCode.Success)
                    {
                        Interlocked.Increment(ref fail);
                    }

                    Interlocked.Increment(ref received);

                    var localAll = Interlocked.Increment(ref all);
                    if (localAll % 1000 == 0)
                    {
                        Console.Write(".");
                    }
                    if (localAll % 100000 == 0)
                    {
                        var elapsed = sw2.Elapsed;
                        sw2.Restart();
                        context.Log.Trace("\nDONE TOTAL {0} WRITES IN {1} ({2:0.0}/s).",
                                          localAll,
                                          elapsed,
                                          1000.0 * 100000 / elapsed.TotalMilliseconds);
                    }
                    if (localAll == requestsCnt)
                    {
                        doneEvent.Set();
                    }
                },
                    connectionClosed: (conn, err) =>
                {
                    if (all < requestsCnt)
                    {
                        context.Fail(null, "Socket was closed, but not all requests were completed.");
                    }
                    else
                    {
                        context.Success();
                    }
                });

                clients.Add(client);

                threads.Add(new Thread(() =>
                {
                    for (int j = 0; j < count; ++j)
                    {
                        var corrid = Guid.NewGuid();
                        var write  = new ClientMessageDto.WriteEvents(
                            Guid.Empty,
                            streams[rnd.Next(streamsCnt)],
                            ExpectedVersion.Any,
                            new[] {
                            new ClientMessageDto.Event(Guid.NewGuid(),
                                                       "TakeSomeSpaceEvent",
                                                       Encoding.UTF8.GetBytes("DATA" + new string('*', 256)),
                                                       Encoding.UTF8.GetBytes("METADATA" + new string('$', 100)))
                        });
                        var package = new TcpPackage(TcpCommand.WriteEvents, corrid, write.Serialize());
                        client.EnqueueSend(package.AsByteArray());

                        Interlocked.Increment(ref sent);
                        while (sent - received > context.Client.Options.WriteWindow)
                        {
                            Thread.Sleep(1);
                        }
                    }
                }));
            }

            var sw = Stopwatch.StartNew();

            sw2.Start();
            foreach (var thread in threads)
            {
                thread.IsBackground = true;
                thread.Start();
            }
            doneEvent.WaitOne();
            sw.Stop();

            foreach (var client in clients)
            {
                client.Close();
            }

            context.Log.Info("Completed. Successes: {0}, failures: {1} (WRONG VERSION: {2}, P: {3}, C: {4}, F: {5}, D: {6})",
                             succ,
                             fail,
                             wrongExpVersion,
                             prepTimeout,
                             commitTimeout,
                             forwardTimeout,
                             streamDeleted);

            var reqPerSec = (all + 0.0) / sw.ElapsedMilliseconds * 1000;

            context.Log.Info("{0} requests completed in {1}ms ({2:0.00} reqs per sec).",
                             all,
                             sw.ElapsedMilliseconds,
                             reqPerSec);

            PerfUtils.LogData(
                Keyword,
                PerfUtils.Row(PerfUtils.Col("clientsCnt", clientsCnt),
                              PerfUtils.Col("requestsCnt", requestsCnt),
                              PerfUtils.Col("ElapsedMilliseconds", sw.ElapsedMilliseconds)),
                PerfUtils.Row(PerfUtils.Col("successes", succ), PerfUtils.Col("failures", fail)));

            PerfUtils.LogTeamCityGraphData(string.Format("{0}-{1}-{2}-reqPerSec", Keyword, clientsCnt, requestsCnt),
                                           (int)reqPerSec);

            PerfUtils.LogTeamCityGraphData(
                string.Format("{0}-{1}-{2}-failureSuccessRate", Keyword, clientsCnt, requestsCnt),
                100 * fail / (fail + succ));

            context.Success();
        }
        public bool Execute(CommandProcessorContext context, string[] args)
        {
            var    eventStreamId   = "test-stream";
            var    expectedVersion = ExpectedVersion.Any;
            var    data            = "test-data";
            string metadata        = null;

            if (args.Length > 0)
            {
                if (args.Length < 3 || args.Length > 4)
                {
                    return(false);
                }
                eventStreamId   = args[0];
                expectedVersion = args[1].ToUpper() == "ANY" ? ExpectedVersion.Any : int.Parse(args[1]);
                data            = args[2];
                if (args.Length == 4)
                {
                    metadata = args[3];
                }
            }

            context.IsAsync();

            var writeDto = new ClientMessageDto.WriteEvents(
                Guid.Empty,
                eventStreamId,
                expectedVersion,
                new[]
            {
                new ClientMessageDto.Event(Guid.NewGuid(),
                                           "TakeSomeSpaceEvent",
                                           Encoding.UTF8.GetBytes(data),
                                           Encoding.UTF8.GetBytes(metadata ?? string.Empty))
            });
            var package = new TcpPackage(TcpCommand.WriteEvents, writeDto.Serialize());

            var sw = new Stopwatch();

            context.Client.CreateTcpConnection(
                context,
                connectionEstablished: conn =>
            {
                context.Log.Info("[{0}]: Writing...", conn.EffectiveEndPoint);
                sw.Start();
                conn.EnqueueSend(package.AsByteArray());
            },
                handlePackage: (conn, pkg) =>
            {
                if (pkg.Command != TcpCommand.WriteEventsCompleted)
                {
                    context.Fail(reason: string.Format("Unexpected TCP package: {0}.", pkg.Command));
                    return;
                }

                sw.Stop();

                var dto = pkg.Data.Deserialize <ClientMessageDto.WriteEventsCompleted>();
                if ((OperationErrorCode)dto.ErrorCode == OperationErrorCode.Success)
                {
                    context.Log.Info("Successfully written. EventId: {0}.", dto.CorrelationId);
                    PerfUtils.LogTeamCityGraphData(string.Format("{0}-latency-ms", Keyword), (int)sw.ElapsedMilliseconds);
                }
                else
                {
                    context.Log.Info("Error while writing: {0} ({1}). EventId: {2}.",
                                     dto.Error,
                                     (OperationErrorCode)dto.ErrorCode,
                                     dto.CorrelationId);
                }

                context.Log.Info("Write request took: {0}.", sw.Elapsed);
                conn.Close();
                context.Success();
            },
                connectionClosed: (connection, error) =>
            {
                if (error == SocketError.Success)
                {
                    context.Success();
                }
                else
                {
                    context.Fail();
                }
            });

            context.WaitForCompletion();
            return(true);
        }
Beispiel #6
0
        private void WriteFlood(CommandProcessorContext context, int clientsCnt, int requestsCnt)
        {
            context.IsAsync();

            var clients        = new List <TcpTypedConnection <byte[]> >();
            var threads        = new List <Thread>();
            var autoResetEvent = new AutoResetEvent(false);

            var succ = 0;
            var fail = 0;
            var all  = 0;

            for (int i = 0; i < clientsCnt; i++)
            {
                var count = requestsCnt / clientsCnt + ((i == clientsCnt - 1) ? requestsCnt % clientsCnt : 0);

                var autoEvent     = new AutoResetEvent(false);
                var eventStreamId = "es" + Guid.NewGuid();

                var client = context.Client.CreateTcpConnection(
                    context,
                    (conn, pkg) =>
                {
                    if (pkg.Command != TcpCommand.WriteEventsCompleted)
                    {
                        context.Fail(reason: string.Format("Unexpected TCP package: {0}.", pkg.Command));
                        return;
                    }

                    var dto = pkg.Data.Deserialize <ClientMessageDto.WriteEventsCompleted>();
                    if (dto.ErrorCode == (int)OperationErrorCode.Success)
                    {
                        if (Interlocked.Increment(ref succ) % 1000 == 0)
                        {
                            Console.Write(".");
                        }
                    }
                    else
                    {
                        Interlocked.Increment(ref fail);
                    }

                    if (Interlocked.Increment(ref all) == requestsCnt)
                    {
                        autoResetEvent.Set();
                    }
                    autoEvent.Set();
                },
                    connectionClosed: (conn, err) =>
                {
                    if (all < requestsCnt)
                    {
                        context.Fail(null, "Socket was closed, but not all requests were completed.");
                    }
                    else
                    {
                        context.Success();
                    }
                });
                clients.Add(client);

                threads.Add(new Thread(() =>
                {
                    for (int j = 0; j < count; ++j)
                    {
                        var write = new ClientMessageDto.WriteEvents(
                            eventStreamId,
                            ExpectedVersion.Any,
                            new[] {
                            new ClientMessageDto.Event(Guid.NewGuid(),
                                                       "TakeSomeSpaceEvent",
                                                       Encoding.UTF8.GetBytes("DATA" + new string('*', 256)),
                                                       Encoding.UTF8.GetBytes("METADATA" + new string('$', 100)))
                        });
                        var package = new TcpPackage(TcpCommand.WriteEvents, Guid.NewGuid(), write.Serialize());
                        client.EnqueueSend(package.AsByteArray());
                        autoEvent.WaitOne();
                    }
                }));
            }

            var sw = Stopwatch.StartNew();

            foreach (var thread in threads)
            {
                thread.IsBackground = true;
                thread.Start();
            }
            autoResetEvent.WaitOne();
            sw.Stop();

            foreach (var client in clients)
            {
                client.Close();
            }

            context.Log.Info("Completed. Successes: {0}, failures: {1}", succ, fail);

            var reqPerSec = (requestsCnt + 0.0) / sw.ElapsedMilliseconds * 1000;

            context.Log.Info("{0} requests completed in {1}ms ({2:0.00} reqs per sec).",
                             all,
                             sw.ElapsedMilliseconds,
                             reqPerSec);

            PerfUtils.LogData(
                Keyword,
                PerfUtils.Row(PerfUtils.Col("clientsCnt", clientsCnt),
                              PerfUtils.Col("requestsCnt", requestsCnt),
                              PerfUtils.Col("ElapsedMilliseconds", sw.ElapsedMilliseconds)),
                PerfUtils.Row(PerfUtils.Col("successes", succ), PerfUtils.Col("failures", fail)));

            PerfUtils.LogTeamCityGraphData(string.Format("{0}-{1}-{2}-reqPerSec", Keyword, clientsCnt, requestsCnt),
                                           (int)reqPerSec);

            PerfUtils.LogTeamCityGraphData(
                string.Format("{0}-{1}-{2}-failureSuccessRate", Keyword, clientsCnt, requestsCnt),
                100 * fail / (fail + succ));

            PerfUtils.LogTeamCityGraphData(string.Format("{0}-latency-ms", Keyword),
                                           (int)(sw.ElapsedMilliseconds / requestsCnt));

            context.Success();
        }
        private void Write(Status status, int writerIdx, CommandProcessorContext context, int requests, AutoResetEvent finish)
        {
            TcpTypedConnection <byte[]> connection;
            var iteration = new AutoResetEvent(false);

            var sent = 0;

            var prepareTimeouts     = 0;
            var commitTimeouts      = 0;
            var forwardTimeouts     = 0;
            var wrongExpctdVersions = 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 <ClientMessageDto.WriteEventsCompleted>();
                switch ((OperationErrorCode)dto.ErrorCode)
                {
                case OperationErrorCode.Success:
                    lock (_heads)
                    {
                        var currentHead = _heads[streamIdx];
                        Ensure.Equal(currentHead, head);
                        _heads[streamIdx]++;
                    }
                    break;

                case OperationErrorCode.PrepareTimeout:
                    prepareTimeouts++;
                    failed++;
                    break;

                case OperationErrorCode.CommitTimeout:
                    commitTimeouts++;
                    failed++;
                    break;

                case OperationErrorCode.ForwardTimeout:
                    forwardTimeouts++;
                    failed++;
                    break;

                case OperationErrorCode.WrongExpectedVersion:
                    wrongExpctdVersions++;
                    failed++;
                    break;

                case OperationErrorCode.StreamDeleted:
                    streamsDeleted++;
                    failed++;
                    break;

                default:
                    throw new ArgumentOutOfRangeException();
                }

                sent++;
                if (sent % 1000 == 0)
                {
                    status.ReportWritesProgress(writerIdx, sent, prepareTimeouts, commitTimeouts, forwardTimeouts,
                                                wrongExpctdVersions, 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 corrid = Guid.NewGuid();
                var evnt   = CreateEvent(_streams[streamIdx], head + 2);
                var write  = new ClientMessageDto.WriteEvents(corrid,
                                                              _streams[streamIdx],
                                                              head == -1 ? head : head + 1,
                                                              new[] { new ClientMessageDto.Event(evnt) });

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

            status.ReportWritesProgress(writerIdx, sent, prepareTimeouts, commitTimeouts, forwardTimeouts,
                                        wrongExpctdVersions, streamsDeleted, failed, requests);
            status.FinilizeStatus(writerIdx, failed != sent);
            connection.Close();
        }
        public bool Execute(CommandProcessorContext context, string[] args)
        {
            var          eventStreamId   = "test-stream";
            const string data            = "test-data";
            var          writeCount      = 10;
            var          expectedVersion = ExpectedVersion.Any;

            if (args.Length > 0)
            {
                if (args.Length > 3)
                {
                    return(false);
                }
                writeCount = int.Parse(args[0]);
                if (args.Length >= 2)
                {
                    eventStreamId = args[1];
                }
                if (args.Length >= 3)
                {
                    expectedVersion = args[2].Trim().ToUpper() == "ANY" ? ExpectedVersion.Any : int.Parse(args[2].Trim());
                }
            }

            context.IsAsync();

            var writeDto = new ClientMessageDto.WriteEvents(
                Guid.Empty,
                eventStreamId,
                expectedVersion,
                Enumerable.Range(0, writeCount).Select(x =>
                                                       new ClientMessageDto.Event(Guid.NewGuid(),
                                                                                  "type",
                                                                                  Encoding.UTF8.GetBytes(data),
                                                                                  new byte[0])).ToArray());

            var package = new TcpPackage(TcpCommand.WriteEvents, writeDto.Serialize());

            var sw = new Stopwatch();

            context.Client.CreateTcpConnection(
                context,
                connectionEstablished: conn =>
            {
                context.Log.Info("[{0}]: Writing...", conn.EffectiveEndPoint);
                sw.Start();
                conn.EnqueueSend(package.AsByteArray());
            },
                handlePackage: (conn, pkg) =>
            {
                if (pkg.Command != TcpCommand.WriteEventsCompleted)
                {
                    context.Fail(reason: string.Format("Unexpected TCP package: {0}.", pkg.Command));
                    return;
                }

                sw.Stop();

                var dto = pkg.Data.Deserialize <ClientMessageDto.WriteEventsCompleted>();
                if (dto.ErrorCode == (int)OperationErrorCode.Success)
                {
                    context.Log.Info("Successfully written {0} events. CorrelationId: {1}.",
                                     writeCount,
                                     dto.CorrelationId);
                    PerfUtils.LogTeamCityGraphData(string.Format("{0}-latency-ms", Keyword), (int)sw.ElapsedMilliseconds);
                }
                else
                {
                    context.Log.Info("Error while writing: {0}. CorrelationId: {1}.", dto.Error, dto.CorrelationId);
                }
                context.Log.Info("Write request took: {0}.", sw.Elapsed);

                conn.Close();
                context.Success();
            },
                connectionClosed: (connection, error) =>
            {
                if (error == SocketError.Success)
                {
                    context.Success();
                }
                else
                {
                    context.Fail();
                }
            });

            context.WaitForCompletion();
            return(true);
        }
Beispiel #9
0
        private void Flood(CommandProcessorContext context,
                           string eventStreamId,
                           int clientsCnt,
                           int minPerSecond,
                           int maxPerSecond,
                           int runTimeMinutes)
        {
            context.IsAsync();

            var clients   = new List <TcpTypedConnection <byte[]> >();
            var threads   = new List <Thread>();
            var doneEvent = new ManualResetEvent(false);
            var done      = false;

            var succ = 0;
            var fail = 0;

            var requestsCnt = 0;

            var watchLockRoot = new object();
            var sw            = Stopwatch.StartNew();

            for (int i = 0; i < clientsCnt; i++)
            {
                int sent     = 0;
                int received = 0;

                var esId = eventStreamId ?? "Stream-" + Thread.CurrentThread.ManagedThreadId % 3;

                var client = context.Client.CreateTcpConnection(
                    context,
                    (conn, pkg) =>
                {
                    if (pkg.Command != TcpCommand.WriteEventsCompleted)
                    {
                        context.Fail(reason: string.Format("Unexpected TCP package: {0}.", pkg.Command));
                        return;
                    }

                    var dto = pkg.Data.Deserialize <ClientMessageDto.WriteEventsCompleted>();
                    if (dto.ErrorCode == (int)OperationErrorCode.Success)
                    {
                        var succDone = Interlocked.Increment(ref succ);
                        if (succDone % maxPerSecond == 0)
                        {
                            Console.Write(".");
                        }

                        Interlocked.Increment(ref requestsCnt);
                    }
                    else
                    {
                        Interlocked.Increment(ref fail);
                    }

                    Interlocked.Increment(ref received);
                },
                    connectionClosed: (conn, err) =>
                {
                    if (!done)
                    {
                        context.Fail(reason: "Socket was closed, but not all requests were completed.");
                    }
                    else
                    {
                        context.Success();
                    }
                });
                clients.Add(client);

                threads.Add(new Thread(() =>
                {
                    var sentCount = 0;
                    var sleepTime = 0;

                    var dataSizeCoefficient = 1;
                    var currentMinute       = -1;

                    while (true)
                    {
                        TimeSpan elapsed;
                        lock (watchLockRoot)
                            elapsed = sw.Elapsed;

                        if (elapsed.TotalMinutes > runTimeMinutes)
                        {
                            done = true;
                            doneEvent.Set();
                            break;
                        }

                        if (sentCount == 0)
                        {
                            int elapsedMinutesInt = (int)elapsed.TotalMinutes;
                            lock (_randomLockRoot)
                            {
                                sentCount = minPerSecond == maxPerSecond
                                            ? maxPerSecond : _random.Next(minPerSecond, maxPerSecond);
                                dataSizeCoefficient = _random.Next(8, 256);
                            }

                            if (currentMinute != elapsedMinutesInt)
                            {
                                currentMinute = elapsedMinutesInt;
                                context.Log.Info("\nElapsed {0} of {1} minutes, sent {2}; next block coef. {3}",
                                                 elapsedMinutesInt,
                                                 runTimeMinutes,
                                                 sent,
                                                 dataSizeCoefficient);
                            }

                            sleepTime = 1000 / sentCount;
                        }

                        var dataSize = dataSizeCoefficient * 8;
                        var write    = new ClientMessageDto.WriteEvents(
                            Guid.Empty,
                            esId,
                            ExpectedVersion.Any,
                            new[] {
                            new ClientMessageDto.Event(
                                Guid.NewGuid(),
                                "TakeSomeSpaceEvent",
                                Encoding.UTF8.GetBytes("DATA" + dataSize.ToString(" 00000 ") + new string('*', dataSize)),
                                Encoding.UTF8.GetBytes("METADATA" + new string('$', 100)))
                        });
                        var package = new TcpPackage(TcpCommand.WriteEvents, write.Serialize());
                        client.EnqueueSend(package.AsByteArray());

                        Interlocked.Increment(ref sent);

                        Thread.Sleep(sleepTime);
                        sentCount -= 1;

                        while (sent - received > context.Client.Options.WriteWindow)
                        {
                            Thread.Sleep(1);
                        }
                    }
                }));
            }

            foreach (var thread in threads)
            {
                thread.IsBackground = true;
                thread.Start();
            }

            doneEvent.WaitOne();
            sw.Stop();

            foreach (var client in clients)
            {
                client.Close();
            }

            context.Log.Info("Completed. Successes: {0}, failures: {1}", succ, fail);
            var reqPerSec = (requestsCnt + 0.0) / sw.ElapsedMilliseconds * 1000;

            context.Log.Info("{0} requests completed in {1}ms ({2:0.00} reqs per sec).",
                             requestsCnt,
                             sw.ElapsedMilliseconds,
                             reqPerSec);

            PerfUtils.LogData(
                Keyword,
                PerfUtils.Row(PerfUtils.Col("clientsCnt", clientsCnt),
                              PerfUtils.Col("requestsCnt", requestsCnt),
                              PerfUtils.Col("ElapsedMilliseconds", sw.ElapsedMilliseconds)),
                PerfUtils.Row(PerfUtils.Col("successes", succ), PerfUtils.Col("failures", fail))
                );

            PerfUtils.LogTeamCityGraphData(string.Format("{0}-{1}-{2}-reqPerSec", Keyword, clientsCnt, requestsCnt),
                                           (int)reqPerSec);

            PerfUtils.LogTeamCityGraphData(
                string.Format("{0}-{1}-{2}-failureSuccessRate", Keyword, clientsCnt, requestsCnt),
                100 * fail / (fail + succ));

            context.Success();
        }