private static void WriteGraph(Context ctx, BackgroundCsvWriter <RequestDurationRecord> writer, RestoreRequestGraph graph)
        {
            if (ctx.ExcludeVariants.Contains(graph.VariantName))
            {
                return;
            }

            var hasRecord = ctx.RestoreResultLookup.TryGetValue(
                (graph.VariantName, graph.SolutionName, graph.FileName),
                out var restoreResultRecord);

            if (!hasRecord)
            {
                ctx.WarningCount++;
                Console.WriteLine($"  WARNING: No metadata found for restore log: ({graph.VariantName}, {graph.SolutionName}, {graph.FileName})");
            }

            int requestIndex = 0;

            foreach (var node in graph.Graph.Nodes)
            {
                if (node.EndRequest == null)
                {
                    continue;
                }

                var operationInfo = GetOperationInfo(ctx, node.StartRequest);

                writer.Add(new RequestDurationRecord
                {
                    VariantName         = graph.VariantName,
                    SolutionName        = graph.SolutionName,
                    TestType            = TestType.Restore,
                    MachineName         = restoreResultRecord?.MachineName ?? ctx.MachineName,
                    LogFileIndex        = ctx.LogFileIndex,
                    LogFileRequestIndex = requestIndex,
                    IsWarmUp            = hasRecord ? restoreResultRecord.IsWarmUp() : (bool?)null,
                    Iteration           = hasRecord ? restoreResultRecord.Iteration : (int?)null,
                    Iterations          = hasRecord ? restoreResultRecord.IterationCount : (int?)null,
                    Method           = node.StartRequest.Method,
                    Url              = node.StartRequest.Url,
                    StatusCode       = (int)node.EndRequest.StatusCode,
                    HeaderDurationMs = node.EndRequest.Duration.TotalMilliseconds,
                    BodyDurationMs   = null,
                    OperationType    = operationInfo?.Operation.Type,
                    PackageId        = (operationInfo?.Operation as OperationWithId)?.Id,
                    PackageVersion   = (operationInfo?.Operation as OperationWithIdVersion)?.Version,
                });

                requestIndex++;
            }

            ctx.LogFileIndex++;
        }
        private static async Task <int> ExecuteAsync(Context ctx)
        {
            using (var writer = new BackgroundCsvWriter <RequestDurationRecord>(ctx.OutputPath, gzip: true))
            {
                foreach (var graph in RestoreLogParser.ParseGraphs(ctx.InputDir, ctx.ExcludeVariants, ctx.StringToString))
                {
                    await UpdateSources(ctx, graph.Graph.Sources);

                    WriteGraph(ctx, writer, graph);
                }

                foreach (var replayLogPath in Directory.EnumerateFiles(ctx.InputDir, $"{ReplayRequestGraph.ReplayLogPrefix}-*-*.csv"))
                {
                    WriteReplayLog(ctx, writer, replayLogPath);
                }
            }

            return(ctx.WarningsAsErrors && ctx.WarningCount > 0 ? 1 : 0);
        }
        private static void WriteReplayLog(Context ctx, BackgroundCsvWriter <RequestDurationRecord> writer, string replayLogPath)
        {
            var fileName = Path.GetFileName(replayLogPath);

            if (!Helper.TryParseFileName(replayLogPath, out var fileType, out var variantName, out var solutionName) ||
                fileType != ReplayRequestGraph.ReplayLogPrefix)
            {
                return;
            }

            if (ctx.ExcludeVariants.Contains(variantName))
            {
                return;
            }

            Console.WriteLine($"Parsing {replayLogPath}...");

            var hasRecord = ctx.ReplayResultLookup.TryGetValue(
                (variantName, solutionName, fileName),
                out var replayResultRecord);

            if (variantName != null)
            {
                Console.WriteLine($"  Variant name:  {variantName}");
            }
            Console.WriteLine($"  Solution name: {solutionName}");

            if (!hasRecord)
            {
                ctx.WarningCount++;
                Console.WriteLine($"  WARNING: No metadata found for replay log: ({variantName}, {solutionName}, {fileName})");
            }

            using (var reader = new StreamReader(replayLogPath))
                using (var csvReader = new CsvReader(reader, CultureInfo.InvariantCulture))
                {
                    int requestIndex = 0;
                    foreach (var record in csvReader.GetRecords <ReplayRequestRecord>())
                    {
                        const string method        = "GET";
                        var          operationInfo = GetOperationInfo(ctx, new StartRequest(method, record.Url));

                        writer.Add(new RequestDurationRecord
                        {
                            VariantName         = variantName,
                            SolutionName        = solutionName,
                            TestType            = TestType.Replay,
                            MachineName         = replayResultRecord?.MachineName ?? ctx.MachineName,
                            LogFileIndex        = ctx.LogFileIndex,
                            LogFileRequestIndex = requestIndex,
                            IsWarmUp            = hasRecord ? replayResultRecord.IsWarmUp : (bool?)null,
                            Iteration           = hasRecord ? replayResultRecord.Iteration : (int?)null,
                            Iterations          = hasRecord ? replayResultRecord.Iterations : (int?)null,
                            Method           = method,
                            Url              = record.Url,
                            StatusCode       = record.StatusCode,
                            HeaderDurationMs = record.HeaderDurationMs,
                            BodyDurationMs   = record.BodyDurationMs,
                            OperationType    = operationInfo?.Operation.Type,
                            PackageId        = (operationInfo?.Operation as OperationWithId)?.Id,
                            PackageVersion   = (operationInfo?.Operation as OperationWithIdVersion)?.Version,
                        });

                        requestIndex++;
                    }

                    Console.WriteLine($"  Request count: {requestIndex:n0}");
                }

            ctx.LogFileIndex++;
        }
Пример #4
0
        private static async Task GetRequestTask(
            Dictionary <RequestNode, Task> nodeToTask,
            SemaphoreSlim throttle,
            object consoleLock,
            HttpClient httpClient,
            RequestNode node,
            BackgroundCsvWriter <ReplayRequestRecord> writer)
        {
            if (node.Dependencies.Any())
            {
                await Task.WhenAll(node.Dependencies.Select(n => nodeToTask[n]).ToList());
            }

            await throttle.WaitAsync();

            try
            {
                if (node.StartRequest.Method != "GET")
                {
                    throw new InvalidOperationException("Only GET requests are supported.");
                }

                var       buffer      = new byte[80 * 1024];
                const int maxAttempts = 3;
                for (var i = 0; i < maxAttempts; i++)
                {
                    var stopwatch = Stopwatch.StartNew();
                    try
                    {
                        using (var response = await httpClient.GetAsync(node.StartRequest.Url, HttpCompletionOption.ResponseHeadersRead))
                        {
                            var headerDuration = stopwatch.Elapsed;

                            using (var stream = await response.Content.ReadAsStreamAsync())
                            {
                                int read;
                                do
                                {
                                    read = await stream.ReadAsync(buffer, 0, buffer.Length);
                                }while (read > 0);
                            }

                            writer.Add(new ReplayRequestRecord
                            {
                                Url              = node.StartRequest.Url,
                                StatusCode       = (int)response.StatusCode,
                                HeaderDurationMs = headerDuration.TotalMilliseconds,
                                BodyDurationMs   = (stopwatch.Elapsed - headerDuration).TotalMilliseconds,
                            });

                            if (!response.IsSuccessStatusCode && response.StatusCode != HttpStatusCode.NotFound)
                            {
                                response.EnsureSuccessStatusCode();
                            }

                            break;
                        }
                    }
                    catch (Exception ex) when(i < maxAttempts - 1)
                    {
                        lock (consoleLock)
                        {
                            Console.WriteLine($"  ERROR {node.StartRequest.Url} {stopwatch.ElapsedMilliseconds}ms");
                            Console.WriteLine(ExceptionUtilities.DisplayMessage(ex, indent: true));
                        }
                    }
                }
            }
            finally
            {
                throttle.Release();
            }
        }
Пример #5
0
        private static async Task ExecuteIterationAsync(
            string rootDir,
            string resultsPath,
            string variantName,
            string solutionName,
            HttpClient httpClient,
            int iterations,
            int maxConcurrency,
            List <RequestNode> topologicalOrder,
            int iteration,
            bool noDependencies)
        {
            var logPrefix = $"[{iteration}/{iterations}{(iteration == 0 ? " (warm-up)" : string.Empty)}]";

            string requestsFileName;

            if (variantName == null)
            {
                requestsFileName = $"{ReplayLogPrefix}-{solutionName}-{Helper.GetLogTimestamp()}.csv";
            }
            else
            {
                requestsFileName = $"{ReplayLogPrefix}-{variantName}-{solutionName}-{Helper.GetLogTimestamp()}.csv";
            }

            var logsDir = Path.Combine(rootDir, "out", "logs");

            if (!Directory.Exists(logsDir))
            {
                Directory.CreateDirectory(logsDir);
            }

            var requestsPath = Path.Combine(logsDir, requestsFileName);

            var stopwatch = new Stopwatch();

            using (var writer = new BackgroundCsvWriter <ReplayRequestRecord>(requestsPath, gzip: false))
            {
                Console.WriteLine($"{logPrefix} Starting...");
                var nodeToTask  = new Dictionary <RequestNode, Task>();
                var throttle    = new SemaphoreSlim(maxConcurrency);
                var consoleLock = new object();

                stopwatch.Start();

                foreach (var node in topologicalOrder)
                {
                    nodeToTask.Add(node, GetRequestTask(nodeToTask, throttle, consoleLock, httpClient, node, writer));
                }

                try
                {
                    await Task.WhenAll(nodeToTask.Values);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("At least one request failed.");
                    Console.WriteLine(ex.ToString());
                }

                stopwatch.Stop();

                Console.WriteLine($"{logPrefix} Completed in {stopwatch.ElapsedMilliseconds}ms.");
            }

            CsvUtility.Append(resultsPath, new ReplayResultRecord
            {
                TimestampUtc   = Helper.GetExcelTimestamp(DateTimeOffset.UtcNow),
                MachineName    = Environment.MachineName,
                Iteration      = iteration,
                IsWarmUp       = iteration == 0,
                Iterations     = iterations,
                VariantName    = variantName,
                SolutionName   = solutionName,
                RequestCount   = topologicalOrder.Count,
                DurationMs     = stopwatch.Elapsed.TotalMilliseconds,
                MaxConcurrency = maxConcurrency,
                LogFileName    = requestsFileName,
                Dependencies   = !noDependencies,
            });
        }
Пример #6
0
        private static int Execute(string inputDir, string outputPath)
        {
            if (string.IsNullOrWhiteSpace(inputDir))
            {
                if (!Helper.TryFindRoot(out var rootDir))
                {
                    return(1);
                }

                var outDir = Path.Combine(rootDir, "out");

                if (string.IsNullOrWhiteSpace(inputDir))
                {
                    inputDir = outDir;
                }
            }

            if (string.IsNullOrWhiteSpace(outputPath))
            {
                outputPath = Path.Combine(inputDir, "merged-test-results.csv");
            }

            Console.WriteLine($"Input directory: {inputDir}");
            Console.WriteLine($"Output path:     {outputPath}");

            var dir = Path.GetDirectoryName(outputPath);

            if (!Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }

            using (var writer = new BackgroundCsvWriter <TestResultRecord>(outputPath, gzip: false))
            {
                var testResultIndex = 0;

                foreach (var restoreResult in CsvUtility.EnumerateRestoreResults(inputDir))
                {
                    if (restoreResult.ScenarioName != "warmup" && restoreResult.ScenarioName != "arctic")
                    {
                        Console.WriteLine($"Skipping restore scenario '{restoreResult.ScenarioName}'.");
                        continue;
                    }

                    writer.Add(new TestResultRecord
                    {
                        TimestampUtc    = Helper.GetExcelTimestamp(DateTimeOffset.Parse(restoreResult.TimestampUtc)),
                        VariantName     = restoreResult.VariantName,
                        SolutionName    = restoreResult.SolutionName,
                        TestType        = TestType.Restore,
                        MachineName     = restoreResult.MachineName,
                        TestResultIndex = testResultIndex,
                        IsWarmUp        = restoreResult.IsWarmUp(),
                        Iteration       = restoreResult.Iteration,
                        Iterations      = restoreResult.IterationCount,
                        DurationMs      = restoreResult.TotalTimeSeconds * 1000,
                        LogFileName     = restoreResult.LogFileName,
                        Dependencies    = true,
                    });

                    testResultIndex++;
                }

                foreach (var replayResult in CsvUtility.EnumerateReplayResults(inputDir))
                {
                    writer.Add(new TestResultRecord
                    {
                        TimestampUtc    = replayResult.TimestampUtc,
                        VariantName     = replayResult.VariantName,
                        SolutionName    = replayResult.SolutionName,
                        TestType        = TestType.Replay,
                        MachineName     = replayResult.MachineName,
                        TestResultIndex = testResultIndex,
                        IsWarmUp        = replayResult.IsWarmUp,
                        Iteration       = replayResult.Iteration,
                        Iterations      = replayResult.Iterations,
                        DurationMs      = replayResult.DurationMs,
                        LogFileName     = replayResult.LogFileName,
                        Dependencies    = replayResult.Dependencies,
                    });

                    testResultIndex++;
                }

                Console.WriteLine($"Wrote {testResultIndex} test results.");
            }

            return(0);
        }