private static TcpPackage WrapTransactionCommit(ClientMessage.TransactionCommit msg)
 {
     var dto = new ClientMessageDto.TransactionCommit(msg.CorrelationId, msg.TransactionId, msg.EventStreamId);
     return new TcpPackage(TcpCommand.TransactionCommit, msg.CorrelationId, dto.Serialize());
 }
        public bool Execute(CommandProcessorContext context, string[] args)
        {
            var eventStreamId = "test-stream";
            var expectedVersion = ExpectedVersion.Any;
            int eventsCnt = 10;

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

            context.IsAsync();

            var sw = new Stopwatch();
            var stage = Stage.AcquiringTransactionId;
            long transactionId = -1;
            var writtenEvents = 0;

            context.Client.CreateTcpConnection(
                    context,
                    connectionEstablished: conn =>
                    {
                        context.Log.Info("[{0}]: Starting transaction...", conn.EffectiveEndPoint);
                        sw.Start();
                        var tranStart = new ClientMessageDto.TransactionStart(Guid.Empty, eventStreamId, expectedVersion);
                        var package = new TcpPackage(TcpCommand.TransactionStart, tranStart.Serialize());
                        conn.EnqueueSend(package.AsByteArray());
                    },
                    handlePackage: (conn, pkg) =>
                    {
                        switch (stage)
                        {
                            case Stage.AcquiringTransactionId:
                            {
                                if (pkg.Command != TcpCommand.TransactionStartCompleted)
                                {
                                    context.Fail(reason: string.Format("Unexpected TCP package: {0}.", pkg.Command));
                                    return;
                                }

                                var dto = pkg.Data.Deserialize<ClientMessageDto.TransactionStartCompleted>();
                                if ((OperationErrorCode)dto.ErrorCode != OperationErrorCode.Success)
                                {
                                    var msg = string.Format("Error while starting transaction: {0} ({1}).", dto.Error, (OperationErrorCode)dto.ErrorCode);
                                    context.Log.Info(msg);
                                    context.Fail(reason: msg);
                                }
                                else
                                {
                                    context.Log.Info("Successfully started transaction. TransactionId: {0}.", dto.TransactionId);
                                    context.Log.Info("Now sending transactional events...", dto.TransactionId);

                                    transactionId = dto.TransactionId;
                                    stage = Stage.Writing;
                                    for (int i = 0; i < eventsCnt; ++i)
                                    {
                                        var writeDto = new ClientMessageDto.TransactionWrite(
                                                Guid.Empty,
                                                transactionId,
                                                eventStreamId,
                                                new[] 
                                                { 
                                                        new ClientMessageDto.Event(Guid.NewGuid() ,
                                                                                   "TakeSomeSpaceEvent",
                                                                                   Encoding.UTF8.GetBytes(Guid.NewGuid().ToString()),
                                                                                   Encoding.UTF8.GetBytes(Guid.NewGuid().ToString()))
                                                });
                                        var package = new TcpPackage(TcpCommand.TransactionWrite, writeDto.Serialize());
                                        conn.EnqueueSend(package.AsByteArray());
                                    }
                                }

                                break;
                            }
                            case Stage.Writing:
                            {
                                if (pkg.Command != TcpCommand.TransactionWriteCompleted)
                                {
                                    context.Fail(reason: string.Format("Unexpected TCP package: {0}.", pkg.Command));
                                    return;
                                }

                                var dto = pkg.Data.Deserialize<ClientMessageDto.TransactionWriteCompleted>();
                                if ((OperationErrorCode)dto.ErrorCode != OperationErrorCode.Success)
                                {
                                    var msg = string.Format("Error while writing transactional event: {0} ({1}).", dto.Error, (OperationErrorCode)dto.ErrorCode);
                                    context.Log.Info(msg);
                                    context.Fail(reason: msg);
                                }
                                else
                                {
                                    writtenEvents += 1;
                                    if (writtenEvents == eventsCnt)
                                    {
                                        context.Log.Info("Written all events. Committing...");

                                        stage = Stage.Committing;
                                        var commitDto = new ClientMessageDto.TransactionCommit(Guid.Empty, transactionId, eventStreamId);
                                        var package = new TcpPackage(TcpCommand.TransactionCommit, commitDto.Serialize());
                                        conn.EnqueueSend(package.AsByteArray());
                                    }
                                }
                                break;
                            }
                            case Stage.Committing:
                            {
                                if (pkg.Command != TcpCommand.TransactionCommitCompleted)
                                {
                                    context.Fail(reason: string.Format("Unexpected TCP package: {0}.", pkg.Command));
                                    return;
                                }

                                sw.Stop();

                                var dto = pkg.Data.Deserialize<ClientMessageDto.TransactionCommitCompleted>();
                                if ((OperationErrorCode)dto.ErrorCode != OperationErrorCode.Success)
                                {
                                    var msg = string.Format("Error while committing transaction: {0} ({1}).", dto.Error, (OperationErrorCode)dto.ErrorCode);
                                    context.Log.Info(msg);
                                    context.Log.Info("Transaction took: {0}.", sw.Elapsed);
                                    context.Fail(reason: msg);
                                }
                                else
                                {
                                    context.Log.Info("Successfully committed transaction [{0}]!", dto.TransactionId);
                                    context.Log.Info("Transaction took: {0}.", sw.Elapsed);
                                    PerfUtils.LogTeamCityGraphData(string.Format("{0}-latency-ms", Keyword), (int)sw.ElapsedMilliseconds);
                                    context.Success();
                                }
                                conn.Close();
                                break;
                            }
                            default:
                                throw new ArgumentOutOfRangeException();
                        }

                    },
                    connectionClosed: (connection, error) =>
                    {
                        if (error == SocketError.Success && stage == Stage.Done)
                            context.Success();
                        else
                            context.Fail();
                    });

            context.WaitForCompletion();
            return true;
        }