From 107f3a488ccb0fd8ab37baa7f821fbbd50135a43 Mon Sep 17 00:00:00 2001 From: Martin Tomka Date: Thu, 22 Jun 2023 08:01:09 +0200 Subject: [PATCH 1/4] Improve HttpClient resilience benchmarks --- .../{Benchmark.cs => HedgingBenchmark.cs} | 8 ++- .../HttpClientFactory.cs | 23 +++++++- .../HttpResilienceBenchmark.cs | 59 +++++++++++++++++++ .../Program.cs | 5 +- 4 files changed, 88 insertions(+), 7 deletions(-) rename bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/{Benchmark.cs => HedgingBenchmark.cs} (90%) create mode 100644 bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/HttpResilienceBenchmark.cs diff --git a/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/Benchmark.cs b/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/HedgingBenchmark.cs similarity index 90% rename from bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/Benchmark.cs rename to bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/HedgingBenchmark.cs index fb565f14cbe..f0f85fe4421 100644 --- a/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/Benchmark.cs +++ b/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/HedgingBenchmark.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -9,11 +10,12 @@ namespace Microsoft.Extensions.Http.Resilience.Bench; -public class Benchmark +public class HedgingBenchmark { - private static HttpRequestMessage Request => new(HttpMethod.Post, "https://bogus"); + private static readonly Uri _uri = new("https://bogus"); + private static HttpRequestMessage Request => new(HttpMethod.Post, _uri); - private System.Net.Http.HttpClient _client = null!; + private HttpClient _client = null!; [GlobalSetup] public void GlobalSetup() diff --git a/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/HttpClientFactory.cs b/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/HttpClientFactory.cs index 6d36543189a..76865f53a5a 100644 --- a/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/HttpClientFactory.cs +++ b/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/HttpClientFactory.cs @@ -4,9 +4,13 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Threading; using Microsoft.Extensions.Compliance.Redaction; using Microsoft.Extensions.Compliance.Testing; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Telemetry.Metering; #pragma warning disable R9A033 // Replace uses of 'Enum.GetName' and 'Enum.ToString' with the '[EnumStrings]' code generator for improved performance @@ -24,6 +28,10 @@ public enum HedgingClientType internal static class HttpClientFactory { + public const string EmptyClient = "Empty"; + + public const string StandardClient = "Standard"; + private const string HedgingEndpoint1 = "http://localhost1"; private const string HedgingEndpoint2 = "http://localhost2"; @@ -34,14 +42,25 @@ public static ServiceProvider InitializeServiceProvider(HedgingClientType client .RegisterMetering() .AddSingleton(NullRedactorProvider.Instance) .AddTransient() - .AddHedging(clientType); + .AddHedging(clientType) + .AddHttpClient(StandardClient, client => client.Timeout = Timeout.InfiniteTimeSpan) + .AddStandardResilienceHandler() + .Services + .AddHttpClient(StandardClient) + .AddHttpMessageHandler() + .Services + .AddHttpClient(EmptyClient, client => client.Timeout = Timeout.InfiniteTimeSpan) + .AddHttpMessageHandler(); + + services.RemoveAll(); + services.AddSingleton(NullLoggerFactory.Instance); return services.BuildServiceProvider(); } private static IServiceCollection AddHedging(this IServiceCollection services, HedgingClientType clientType) { - var clientBuilder = services.AddHttpClient(clientType.ToString()); + var clientBuilder = services.AddHttpClient(clientType.ToString(), client => client.Timeout = Timeout.InfiniteTimeSpan); var hedgingBuilder = clientBuilder.AddStandardHedgingHandler().SelectPipelineByAuthority(SimpleClassifications.PublicData); _ = clientBuilder.AddHttpMessageHandler(); diff --git a/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/HttpResilienceBenchmark.cs b/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/HttpResilienceBenchmark.cs new file mode 100644 index 00000000000..0ba41b2cb5f --- /dev/null +++ b/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/HttpResilienceBenchmark.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Globalization; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using BenchmarkDotNet.Attributes; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.Extensions.Http.Resilience.Bench; + +public class HttpResilienceBenchmark +{ + private static readonly Uri _uri = new("https://bogus"); + + private static HttpRequestMessage Request + { + get + { + var request = new HttpRequestMessage(HttpMethod.Post, _uri); + request.Options.Set(new HttpRequestOptionsKey("dummy"), "dummy"); + return request; + } + } + + private HttpClient _client = null!; + private HttpClient _standardClient = null!; + private HttpClient _hedgingClient = null!; + + [GlobalSetup] + public void GlobalSetup() + { + var serviceProvider = HttpClientFactory.InitializeServiceProvider(HedgingClientType.Ordered); + var factory = serviceProvider.GetRequiredService(); + _client = factory.CreateClient(HttpClientFactory.EmptyClient); + _standardClient = factory.CreateClient(HttpClientFactory.StandardClient); + _hedgingClient = factory.CreateClient(nameof(HedgingClientType.Ordered)); + } + + [Benchmark(Baseline = true)] + public Task DefaultClient() + { + return _client.SendAsync(Request, CancellationToken.None); + } + + [Benchmark] + public Task StandardResilienceHandler() + { + return _standardClient.SendAsync(Request, CancellationToken.None); + } + + [Benchmark] + public Task StandardHedgingHandler() + { + return _hedgingClient.SendAsync(Request, CancellationToken.None); + } +} diff --git a/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/Program.cs b/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/Program.cs index af0e0367aff..9a4f9a8cbd8 100644 --- a/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/Program.cs +++ b/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/Program.cs @@ -15,8 +15,9 @@ private static void Main(string[] args) { var doNotRequireSlnToRunBenchmarks = ManualConfig .Create(DefaultConfig.Instance) - .AddJob(Job.MediumRun.WithToolchain(InProcessEmitToolchain.Instance).WithIterationCount(100)) - .AddDiagnoser(MemoryDiagnoser.Default); + .AddJob(Job.MediumRun.WithToolchain(InProcessEmitToolchain.Instance)) + .AddDiagnoser(MemoryDiagnoser.Default) + .WithOptions(ConfigOptions.DisableOptimizationsValidator); BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, doNotRequireSlnToRunBenchmarks); } From 3aecb85907c87e587c4453d010897d6921752207 Mon Sep 17 00:00:00 2001 From: Martin Tomka Date: Thu, 22 Jun 2023 08:07:24 +0200 Subject: [PATCH 2/4] cleanup --- .../Program.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/Program.cs b/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/Program.cs index 9a4f9a8cbd8..af0e0367aff 100644 --- a/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/Program.cs +++ b/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/Program.cs @@ -15,9 +15,8 @@ private static void Main(string[] args) { var doNotRequireSlnToRunBenchmarks = ManualConfig .Create(DefaultConfig.Instance) - .AddJob(Job.MediumRun.WithToolchain(InProcessEmitToolchain.Instance)) - .AddDiagnoser(MemoryDiagnoser.Default) - .WithOptions(ConfigOptions.DisableOptimizationsValidator); + .AddJob(Job.MediumRun.WithToolchain(InProcessEmitToolchain.Instance).WithIterationCount(100)) + .AddDiagnoser(MemoryDiagnoser.Default); BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, doNotRequireSlnToRunBenchmarks); } From 466f58b27696032d5e5a666a20c7056112662ce8 Mon Sep 17 00:00:00 2001 From: Martin Tomka Date: Thu, 22 Jun 2023 09:10:25 +0200 Subject: [PATCH 3/4] fixes --- .../HttpResilienceBenchmark.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/HttpResilienceBenchmark.cs b/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/HttpResilienceBenchmark.cs index 0ba41b2cb5f..fdb23d4e3a6 100644 --- a/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/HttpResilienceBenchmark.cs +++ b/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/HttpResilienceBenchmark.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Globalization; using System.Net.Http; using System.Threading; using System.Threading.Tasks; From 949a1141091aee344ed79964e0dc1c6ca8d950e3 Mon Sep 17 00:00:00 2001 From: Martin Tomka Date: Thu, 22 Jun 2023 15:27:59 +0200 Subject: [PATCH 4/4] PR comments --- .../HttpClientFactory.cs | 4 ++-- .../HttpResilienceBenchmark.cs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/HttpClientFactory.cs b/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/HttpClientFactory.cs index 76865f53a5a..43bae55a908 100644 --- a/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/HttpClientFactory.cs +++ b/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/HttpClientFactory.cs @@ -28,9 +28,9 @@ public enum HedgingClientType internal static class HttpClientFactory { - public const string EmptyClient = "Empty"; + internal const string EmptyClient = "Empty"; - public const string StandardClient = "Standard"; + internal const string StandardClient = "Standard"; private const string HedgingEndpoint1 = "http://localhost1"; private const string HedgingEndpoint2 = "http://localhost2"; diff --git a/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/HttpResilienceBenchmark.cs b/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/HttpResilienceBenchmark.cs index fdb23d4e3a6..6f719b3cc8e 100644 --- a/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/HttpResilienceBenchmark.cs +++ b/bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/HttpResilienceBenchmark.cs @@ -14,6 +14,10 @@ public class HttpResilienceBenchmark { private static readonly Uri _uri = new("https://bogus"); + private HttpClient _client = null!; + private HttpClient _standardClient = null!; + private HttpClient _hedgingClient = null!; + private static HttpRequestMessage Request { get @@ -24,10 +28,6 @@ private static HttpRequestMessage Request } } - private HttpClient _client = null!; - private HttpClient _standardClient = null!; - private HttpClient _hedgingClient = null!; - [GlobalSetup] public void GlobalSetup() {