Skip to content

Commit

Permalink
Cleanup Microsoft.Extensions.Http.Resilience internals (#4141)
Browse files Browse the repository at this point in the history
  • Loading branch information
martintmk authored Jun 30, 2023
1 parent 12be3be commit 50330cb
Show file tree
Hide file tree
Showing 51 changed files with 450 additions and 550 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.Extensions.Http.Resilience.Bench;

internal sealed class EmptyHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ public enum HedgingClientType
internal static class HttpClientFactory
{
internal const string EmptyClient = "Empty";

internal const string StandardClient = "Standard";
internal const string SingleHandlerClient = "SingleHandler";

private const string HedgingEndpoint1 = "http://localhost1";
private const string HedgingEndpoint2 = "http://localhost2";
Expand All @@ -47,10 +47,14 @@ public static ServiceProvider InitializeServiceProvider(HedgingClientType client
.AddStandardResilienceHandler()
.Services
.AddHttpClient(StandardClient)
.AddHttpMessageHandler<NoRemoteCallHandler>()
.ConfigurePrimaryHttpMessageHandler(() => new NoRemoteCallHandler())
.Services
.AddHttpClient(EmptyClient, client => client.Timeout = Timeout.InfiniteTimeSpan)
.AddHttpMessageHandler<NoRemoteCallHandler>();
.ConfigurePrimaryHttpMessageHandler(() => new NoRemoteCallHandler())
.Services
.AddHttpClient(SingleHandlerClient, client => client.Timeout = Timeout.InfiniteTimeSpan)
.AddHttpMessageHandler(() => new EmptyHandler())
.ConfigurePrimaryHttpMessageHandler(() => new NoRemoteCallHandler());

services.RemoveAll<ILoggerFactory>();
services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class HttpResilienceBenchmark

private HttpClient _client = null!;
private HttpClient _standardClient = null!;
private HttpClient _singleHandlerClient = null!;
private HttpClient _hedgingClient = null!;

private static HttpRequestMessage Request
Expand All @@ -35,6 +36,7 @@ public void GlobalSetup()
var factory = serviceProvider.GetRequiredService<IHttpClientFactory>();
_client = factory.CreateClient(HttpClientFactory.EmptyClient);
_standardClient = factory.CreateClient(HttpClientFactory.StandardClient);
_singleHandlerClient = factory.CreateClient(HttpClientFactory.SingleHandlerClient);
_hedgingClient = factory.CreateClient(nameof(HedgingClientType.Ordered));
}

Expand All @@ -55,4 +57,10 @@ public Task<HttpResponseMessage> StandardHedgingHandler()
{
return _hedgingClient.SendAsync(Request, CancellationToken.None);
}

[Benchmark]
public Task<HttpResponseMessage> SingleHandler()
{
return _singleHandlerClient.SendAsync(Request, CancellationToken.None);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using System.Collections.Generic;
using System.Diagnostics.Metrics;

namespace Microsoft.Extensions.Resilience.Test.Resilience;
namespace Microsoft.Extensions.Resilience.Bench;

internal class MeteringUtil
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@
<RootNamespace>Microsoft.Extensions.Resilience</RootNamespace>
<Description>Benchmarks for Microsoft.Extensions.Resilience.</Description>
</PropertyGroup>

<ItemGroup>
<Compile Include="..\..\..\test\Libraries\Microsoft.Extensions.Resilience.Tests\Resilience\MeteringUtil.cs" Link="MeteringUtil.cs" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\Libraries\Microsoft.Extensions.Resilience\Microsoft.Extensions.Resilience.csproj" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using BenchmarkDotNet.Attributes;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.ExceptionSummarization;
using Microsoft.Extensions.Resilience.Test.Resilience;
using Polly;
using Polly.Registry;
using Polly.Telemetry;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ public static IStandardHedgingHandlerBuilder AddStandardHedgingHandler(this IHtt

var optionsName = builder.Name;
var routingBuilder = new RoutingStrategyBuilder(builder.Name, builder.Services);
builder.Services.TryAddSingleton<IRequestCloner, RequestCloner>();

builder.Services.TryAddSingleton<Randomizer>();

_ = builder.Services.AddValidatedOptions<HttpStandardHedgingResilienceOptions, HttpStandardHedgingResilienceOptionsValidator>(optionsName);
_ = builder.Services.AddValidatedOptions<HttpStandardHedgingResilienceOptions, HttpStandardHedgingResilienceOptionsCustomValidator>(optionsName);
_ = builder.Services.PostConfigure<HttpStandardHedgingResilienceOptions>(optionsName, options =>
Expand All @@ -96,7 +98,7 @@ public static IStandardHedgingHandlerBuilder AddStandardHedgingHandler(this IHtt
return null;
}

var requestMessage = snapshot.Create().ReplaceHost(route);
var requestMessage = snapshot.CreateRequestMessage().ReplaceHost(route);

// replace the request message
args.ActionContext.Properties.Set(ResilienceKeys.RequestMessage, requestMessage);
Expand All @@ -110,10 +112,11 @@ public static IStandardHedgingHandlerBuilder AddStandardHedgingHandler(this IHtt
{
var options = context.GetOptions<HttpStandardHedgingResilienceOptions>(optionsName);
context.EnableReloads<HttpStandardHedgingResilienceOptions>(optionsName);
var routingOptions = context.GetOptions<RequestRoutingOptions>(routingBuilder.Name);

_ = builder
.AddStrategy(new RoutingResilienceStrategy(context.ServiceProvider.GetRoutingFactory(routingBuilder.Name)))
.AddStrategy(new RequestMessageSnapshotStrategy(context.ServiceProvider.GetRequiredService<IRequestCloner>()))
.AddStrategy(new RoutingResilienceStrategy(routingOptions.RoutingStrategyProvider!))
.AddStrategy(new RequestMessageSnapshotStrategy())
.AddTimeout(options.TotalRequestTimeoutOptions)
.AddHedging(options.HedgingOptions);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,6 @@ namespace Microsoft.Extensions.Http.Resilience.Internal;
/// </summary>
internal sealed class RequestMessageSnapshotStrategy : ResilienceStrategy
{
private readonly IRequestCloner _requestCloner;

public RequestMessageSnapshotStrategy(IRequestCloner requestCloner)
{
_requestCloner = requestCloner;
}

protected override async ValueTask<Outcome<TResult>> ExecuteCoreAsync<TResult, TState>(
Func<ResilienceContext, TState, ValueTask<Outcome<TResult>>> callback,
ResilienceContext context,
Expand All @@ -32,7 +25,7 @@ protected override async ValueTask<Outcome<TResult>> ExecuteCoreAsync<TResult, T
Throw.InvalidOperationException("The HTTP request message was not found in the resilience context.");
}

using var snapshot = _requestCloner.CreateSnapshot(request);
using var snapshot = RequestMessageSnapshot.Create(request);

context.Properties.Set(ResilienceKeys.RequestSnapshot, snapshot);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,18 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

namespace Microsoft.Extensions.Http.Resilience.Internal.Validators;

internal sealed class HttpStandardHedgingResilienceOptionsCustomValidator : IValidateOptions<HttpStandardHedgingResilienceOptions>
{
private const int CircuitBreakerTimeoutMultiplier = 2;
private readonly INamedServiceProvider<IRequestRoutingStrategyFactory> _namedServiceProvider;

public HttpStandardHedgingResilienceOptionsCustomValidator(INamedServiceProvider<IRequestRoutingStrategyFactory> namedServiceProvider)
{
_namedServiceProvider = namedServiceProvider;
}

public ValidateOptionsResult Validate(string? name, HttpStandardHedgingResilienceOptions options)
{
var builder = new ValidateOptionsResultBuilder();

if (_namedServiceProvider.GetService(name!) is null)
{
builder.AddError($"The hedging routing is not configured for '{name}' HTTP client.");
}

if (options.EndpointOptions.TimeoutOptions.Timeout > options.TotalRequestTimeoutOptions.Timeout)
{
builder.AddError($"Total request timeout strategy must have a greater timeout than the attempt timeout strategy. " +
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// 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.Options;

namespace Microsoft.Extensions.Http.Resilience.Internal;

/// <summary>
/// The cache for named options that allows accessing the last valid options instance.
/// </summary>
/// <typeparam name="TOptions">The type of options.</typeparam>
internal sealed class NamedOptionsCache<TOptions>
{
public NamedOptionsCache(string optionsName, IOptionsMonitor<TOptions> optionsMonitor)
{
Options = optionsMonitor.Get(optionsName);

_ = optionsMonitor.OnChange((options, name) =>
{
if (name == optionsName)
{
Options = options;
}
});
}

public TOptions Options { get; private set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@
namespace Microsoft.Extensions.Http.Resilience.Internal;

#pragma warning disable CA5394 // Do not use insecure randomness
#pragma warning disable CPR138 // Random class instances should be shared as statics.

internal sealed class Randomizer : IRandomizer
internal class Randomizer
{
private static readonly ThreadLocal<Random> _randomInstance = new(() => new Random());

public double NextDouble(double maxValue) => _randomInstance.Value!.NextDouble() * maxValue;
public virtual double NextDouble(double maxValue) => _randomInstance.Value!.NextDouble() * maxValue;

public int NextInt(int maxValue) => _randomInstance.Value!.Next(maxValue);
public virtual int NextInt(int maxValue) => _randomInstance.Value!.Next(maxValue);
}

This file was deleted.

Loading

0 comments on commit 50330cb

Please sign in to comment.