From fa34b2b58946e5a4ba8b8ccecc145db821ff2880 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 23 Oct 2024 21:16:29 +0800 Subject: [PATCH 1/2] Add commands to stress playground --- .../Stress/Stress.ApiService/TraceCreator.cs | 4 +- playground/Stress/Stress.AppHost/Program.cs | 12 ++- .../ResourceBuilderExtensions.cs | 80 +++++++++++++++++++ 3 files changed, 92 insertions(+), 4 deletions(-) create mode 100644 playground/Stress/Stress.AppHost/ResourceBuilderExtensions.cs diff --git a/playground/Stress/Stress.ApiService/TraceCreator.cs b/playground/Stress/Stress.ApiService/TraceCreator.cs index c8b08c60a8e..687b6f40fdd 100644 --- a/playground/Stress/Stress.ApiService/TraceCreator.cs +++ b/playground/Stress/Stress.ApiService/TraceCreator.cs @@ -34,7 +34,7 @@ public async Task CreateTraceAsync(int count, bool createChildren) { var activityStack = new Stack(); - for (var i = 0; i < 10; i++) + for (var i = 0; i < count; i++) { if (i > 0) { @@ -54,8 +54,6 @@ public async Task CreateTraceAsync(int count, bool createChildren) { await CreateChildActivityAsync(name); } - - await Task.Delay(Random.Shared.Next(10, 50)); } while (activityStack.Count > 0) diff --git a/playground/Stress/Stress.AppHost/Program.cs b/playground/Stress/Stress.AppHost/Program.cs index 9c32f26064b..dd1f91d2bc5 100644 --- a/playground/Stress/Stress.AppHost/Program.cs +++ b/playground/Stress/Stress.AppHost/Program.cs @@ -1,7 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Microsoft.Extensions.DependencyInjection; + var builder = DistributedApplication.CreateBuilder(args); +builder.Services.AddHttpClient(); for (var i = 0; i < 10; i++) { @@ -27,12 +30,19 @@ iconName: "CloudDatabase", isHighlighted: true); -for (var i = 0; i < 30; i++) +serviceBuilder.WithHttpEndpoint(5180, name: $"http"); +for (var i = 1; i <= 30; i++) { var port = 5180 + i; serviceBuilder.WithHttpEndpoint(port, name: $"http-{port}"); } +serviceBuilder.WithHttpCommand("/write-console", "Write to console", method: HttpMethod.Get); +serviceBuilder.WithHttpCommand("/increment-counter", "Increment counter", method: HttpMethod.Get); +serviceBuilder.WithHttpCommand("/big-trace", "Big trace", method: HttpMethod.Get); +serviceBuilder.WithHttpCommand("/trace-limit", "Trace limit", method: HttpMethod.Get); +serviceBuilder.WithHttpCommand("/log-message", "Log message", method: HttpMethod.Get); + builder.AddProject("stress-telemetryservice"); #if !SKIP_DASHBOARD_REFERENCE diff --git a/playground/Stress/Stress.AppHost/ResourceBuilderExtensions.cs b/playground/Stress/Stress.AppHost/ResourceBuilderExtensions.cs new file mode 100644 index 00000000000..87b432b95ce --- /dev/null +++ b/playground/Stress/Stress.AppHost/ResourceBuilderExtensions.cs @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.DependencyInjection; + +internal static class ResourceBuilderExtensions +{ + /// + /// Adds a command to the resource that sends an HTTP request to the specified path. + /// + public static IResourceBuilder WithHttpsCommand(this IResourceBuilder builder, + string path, + string displayName, + HttpMethod? method = default, + string? endpointName = default, + string? iconName = default) + where TResource : IResourceWithEndpoints + => WithHttpCommandImpl(builder, path, displayName, endpointName ?? "https", method, "https", iconName); + + /// + /// Adds a command to the resource that sends an HTTP request to the specified path. + /// + public static IResourceBuilder WithHttpCommand(this IResourceBuilder builder, + string path, + string displayName, + HttpMethod? method = default, + string? endpointName = default, + string? iconName = default) + where TResource : IResourceWithEndpoints + => WithHttpCommandImpl(builder, path, displayName, endpointName ?? "http", method, "http", iconName); + + private static IResourceBuilder WithHttpCommandImpl(this IResourceBuilder builder, + string path, + string displayName, + string endpointName, + HttpMethod? method, + string expectedScheme, + string? iconName = default) + where TResource : IResourceWithEndpoints + { + method ??= HttpMethod.Post; + + var endpoints = builder.Resource.GetEndpoints(); + var endpoint = endpoints.FirstOrDefault(e => string.Equals(e.EndpointName, endpointName, StringComparison.OrdinalIgnoreCase)) + ?? throw new DistributedApplicationException($"Could not create HTTP command for resource '{builder.Resource.Name}' as no endpoint named '{endpointName}' was found."); + + var commandType = $"http-{method.Method.ToLowerInvariant()}-{path.ToLowerInvariant()}-request"; + + builder.WithCommand(commandType, displayName, async context => + { + if (!endpoint.IsAllocated) + { + return new ExecuteCommandResult { Success = false, ErrorMessage = "Endpoints are not yet allocated." }; + } + + if (!string.Equals(endpoint.Scheme, expectedScheme, StringComparison.OrdinalIgnoreCase)) + { + return new ExecuteCommandResult { Success = false, ErrorMessage = $"The endpoint named '{endpointName}' on resource '{builder.Resource.Name}' does not have the expected scheme of '{expectedScheme}'." }; + } + + var uri = new UriBuilder(endpoint.Url) { Path = path }.Uri; + var httpClient = context.ServiceProvider.GetRequiredService().CreateClient(); + var request = new HttpRequestMessage(method, uri); + try + { + var response = await httpClient.SendAsync(request, context.CancellationToken); + response.EnsureSuccessStatusCode(); + } + catch (Exception ex) + { + return new ExecuteCommandResult { Success = false, ErrorMessage = ex.Message }; + } + return new ExecuteCommandResult { Success = true }; + }, + iconName: iconName, + iconVariant: IconVariant.Regular); + + return builder; + } +} From 480288d1253a8d8900d1933e4db2b1199c888dea Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Fri, 25 Oct 2024 11:26:56 +0800 Subject: [PATCH 2/2] Update --- playground/Stress/Stress.ApiService/Program.cs | 14 ++++++++++---- playground/Stress/Stress.AppHost/Program.cs | 11 ++++++----- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/playground/Stress/Stress.ApiService/Program.cs b/playground/Stress/Stress.ApiService/Program.cs index bcf9429ebbe..6d8d1cb0bf6 100644 --- a/playground/Stress/Stress.ApiService/Program.cs +++ b/playground/Stress/Stress.ApiService/Program.cs @@ -95,13 +95,19 @@ return $"Sent requests to {string.Join(';', urls)}"; }); -app.MapGet("/log-message-limit", ([FromServices] ILogger logger) => +app.MapGet("/log-message-limit", async ([FromServices] ILogger logger) => { - const int LogCount = 20_000; + const int LogCount = 10_000; + const int BatchSize = 10; - for (var i = 0; i < LogCount; i++) + for (var i = 0; i < LogCount / BatchSize; i++) { - logger.LogInformation("Log entry {LogEntryIndex}", i); + for (var j = 0; j < BatchSize; j++) + { + logger.LogInformation("Log entry {BatchIndex}-{LogEntryIndex}", i, j); + } + + await Task.Delay(100); } return $"Created {LogCount} logs."; diff --git a/playground/Stress/Stress.AppHost/Program.cs b/playground/Stress/Stress.AppHost/Program.cs index dd1f91d2bc5..987637bcb16 100644 --- a/playground/Stress/Stress.AppHost/Program.cs +++ b/playground/Stress/Stress.AppHost/Program.cs @@ -37,11 +37,12 @@ serviceBuilder.WithHttpEndpoint(port, name: $"http-{port}"); } -serviceBuilder.WithHttpCommand("/write-console", "Write to console", method: HttpMethod.Get); -serviceBuilder.WithHttpCommand("/increment-counter", "Increment counter", method: HttpMethod.Get); -serviceBuilder.WithHttpCommand("/big-trace", "Big trace", method: HttpMethod.Get); -serviceBuilder.WithHttpCommand("/trace-limit", "Trace limit", method: HttpMethod.Get); -serviceBuilder.WithHttpCommand("/log-message", "Log message", method: HttpMethod.Get); +serviceBuilder.WithHttpCommand("/write-console", "Write to console", method: HttpMethod.Get, iconName: "ContentViewGalleryLightning"); +serviceBuilder.WithHttpCommand("/increment-counter", "Increment counter", method: HttpMethod.Get, iconName: "ContentViewGalleryLightning"); +serviceBuilder.WithHttpCommand("/big-trace", "Big trace", method: HttpMethod.Get, iconName: "ContentViewGalleryLightning"); +serviceBuilder.WithHttpCommand("/trace-limit", "Trace limit", method: HttpMethod.Get, iconName: "ContentViewGalleryLightning"); +serviceBuilder.WithHttpCommand("/log-message", "Log message", method: HttpMethod.Get, iconName: "ContentViewGalleryLightning"); +serviceBuilder.WithHttpCommand("/log-message-limit", "Log message limit", method: HttpMethod.Get, iconName: "ContentViewGalleryLightning"); builder.AddProject("stress-telemetryservice");