-
Notifications
You must be signed in to change notification settings - Fork 850
Description
Description
When registering the standard resilience handler using builder.Services.ConfigureHttpClientDefaults(x => x.AddStandardResilienceHandler), all HttpClients seem to be using a shared circuit breaker. When one client is causing the circuit to open, the circuit opens for all clients.
Reproduction Steps
Here is my project file:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.2.0" />
</ItemGroup>
</Project>Here is my appsettings.json (modified to be able to more easily reproduce the issue):
{
"RetryOptions": {
"Retry": {
"Delay": "00:00:00",
"MaxRetryAttempts": 1
}
}
}And here is the Program.cs:
var builder = WebApplication.CreateBuilder(args);
var section = builder.Configuration.GetSection("RetryOptions");
var succeedingClientBuilder = builder.Services.AddHttpClient<SucceedingClient>(x =>
x.BaseAddress = new Uri("https://ipv4.icanhazip.com/"));
var failingClientBuilder = builder.Services.AddHttpClient<FailingClient>(x =>
x.BaseAddress = new Uri("http://the-internet.herokuapp.com/status_codes/500"));
var useHttpClientDefaults = true;
if (useHttpClientDefaults)
{
builder.Services.ConfigureHttpClientDefaults(x => x.AddStandardResilienceHandler(section));
}
else
{
succeedingClientBuilder.AddStandardResilienceHandler(section);
failingClientBuilder.AddStandardResilienceHandler(section);
}
var app = builder.Build();
app.MapGet("/test", async (SucceedingClient succeedingClient, FailingClient failingClient) =>
{
var succeedingClientResult = await succeedingClient.Get();
var failingClientResult = await failingClient.Get();
return $"""
SucceedingClient result: {succeedingClientResult}
FailingClient result: {failingClientResult}
""";
});
app.Run();
public class SucceedingClient(HttpClient client)
{
public async Task<string> Get()
{
try
{
using var response = await client.GetAsync("");
return $"Status code {response.StatusCode}";
}
catch (Exception e)
{
return $"Exception {e.Message}";
}
}
}
public class FailingClient(HttpClient client)
{
public async Task<string> Get()
{
try
{
using var response = await client.GetAsync("");
return $"Status code {response.StatusCode}";
}
catch (Exception e)
{
return $"Exception {e.Message}";
}
}
}Run this sample code and continuously hit the /test endpoint until the circuit breaker kicks in.
Expected behavior
I would expect the circuit would only open for the client that is facing issues. From the reproduction steps, the circuit should not open for SucceedingClient when it opens for FailingClient.
Actual behavior
The circuit breaker opens for both the SucceedingClient and the FailingClient, even though only FailingClient is receiving status code 500.
Regression?
No response
Known Workarounds
Use AddStandardResilienceHandler on each HttpClient instead of using ConfigureHttpClientDefaults.
Configuration
dotnet --info
.NET SDK: Version: 8.0.200 Commit: 438cab6a9d Workload version: 8.0.200-manifests.e575128cRuntime Environment:
OS Name: Windows
OS Version: 10.0.22631
OS Platform: Windows
RID: win-x64
Base Path: C:\Program Files\dotnet\sdk\8.0.200\
.NET workloads installed:
There are no installed workloads to display.
Host:
Version: 8.0.2
Architecture: x64
Commit: 1381d5ebd2
.NET SDKs installed:
8.0.200 [C:\Program Files\dotnet\sdk]
.NET runtimes installed:
Microsoft.AspNetCore.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 6.0.27 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 7.0.16 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 8.0.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 6.0.27 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 7.0.16 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 8.0.2 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 6.0.27 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 7.0.16 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 8.0.2 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Other architectures found:
x86 [C:\Program Files (x86)\dotnet]
registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x86\InstallLocation]
Environment variables:
Not set
global.json file:
Not found
Other information
No response