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 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(); } }
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++; }
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); }