From 7ad9a739ae7845bfd94a579bc202987bff7670a4 Mon Sep 17 00:00:00 2001 From: Evgeniy Drenkov Date: Tue, 4 Apr 2023 15:58:03 +0300 Subject: [PATCH 1/6] Added additional break condition setting --- .../CircuitBreakerPolicyTests.cs | 24 +++++++++++++++++++ .../CircuitBreakerPolicySettings.cs | 5 ++++ .../PoliciesHttpClientBuilderExtensions.cs | 2 +- .../ResiliencePoliciesSettings.cs | 8 +++++++ 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/Dodo.HttpClient.ResiliencePolicies.Tests/CircuitBreakerPolicyTests.cs b/src/Dodo.HttpClient.ResiliencePolicies.Tests/CircuitBreakerPolicyTests.cs index 111f270..0688cc8 100644 --- a/src/Dodo.HttpClient.ResiliencePolicies.Tests/CircuitBreakerPolicyTests.cs +++ b/src/Dodo.HttpClient.ResiliencePolicies.Tests/CircuitBreakerPolicyTests.cs @@ -63,6 +63,30 @@ await Helper.InvokeMultipleHttpRequests(wrapper.Client, taskCount, Assert.AreEqual(minimumThroughput + taskCount, wrapper.NumberOfCalls); } + [Test] + public void Should_not_break_on_429() + { + const int retryCount = 5; + const int minimumThroughput = 2; + var settings = new ResiliencePoliciesSettings + { + OverallTimeout = TimeSpan.FromSeconds(5), + RetryPolicySettings = RetryPolicySettings.Constant(retryCount, TimeSpan.FromMilliseconds(100)), + CircuitBreakerPolicySettings = BuildCircuitBreakerSettings(minimumThroughput), + AdditionalFailureResultFilter = _ => false, // no additional filter (default is true for 429) + }; + var wrapper = Create.HttpClientWrapperWrapperBuilder + .WithStatusCode(HttpStatusCode.TooManyRequests) + .WithResiliencePolicySettings(settings) + .Please(); + + const int taskCount = 4; + Assert.DoesNotThrowAsync(async () => + await Helper.InvokeMultipleHttpRequests(wrapper.Client, taskCount)); + + Assert.AreEqual(wrapper.NumberOfCalls, taskCount); + } + private static CircuitBreakerPolicySettings BuildCircuitBreakerSettings(int throughput) { return new CircuitBreakerPolicySettings( diff --git a/src/Dodo.HttpClient.ResiliencePolicies/CircuitBreakerPolicy/CircuitBreakerPolicySettings.cs b/src/Dodo.HttpClient.ResiliencePolicies/CircuitBreakerPolicy/CircuitBreakerPolicySettings.cs index 41630f4..827ecce 100644 --- a/src/Dodo.HttpClient.ResiliencePolicies/CircuitBreakerPolicy/CircuitBreakerPolicySettings.cs +++ b/src/Dodo.HttpClient.ResiliencePolicies/CircuitBreakerPolicy/CircuitBreakerPolicySettings.cs @@ -1,4 +1,5 @@ using System; +using System.Net; using System.Net.Http; using Polly; @@ -14,6 +15,7 @@ public sealed class CircuitBreakerPolicySettings internal Action, TimeSpan> OnBreak { get; set; } internal Action OnReset { get; set; } internal Action OnHalfOpen { get; set; } + internal Func AdditionalFailureResultFilter { get; set; } public CircuitBreakerPolicySettings() : this( @@ -38,10 +40,13 @@ public CircuitBreakerPolicySettings( OnBreak = DoNothingOnBreak; OnReset = DoNothingOnReset; OnHalfOpen = DoNothingOnHalfOpen; + AdditionalFailureResultFilter = HandleTooManyRequests; } private static readonly Action, TimeSpan> DoNothingOnBreak = (_, __) => { }; private static readonly Action DoNothingOnReset = () => { }; private static readonly Action DoNothingOnHalfOpen = () => { }; + private static readonly Func HandleTooManyRequests = response => + response.StatusCode == (HttpStatusCode)429; // Too Many Requests } } diff --git a/src/Dodo.HttpClient.ResiliencePolicies/PoliciesHttpClientBuilderExtensions.cs b/src/Dodo.HttpClient.ResiliencePolicies/PoliciesHttpClientBuilderExtensions.cs index 77c468f..45e45cd 100644 --- a/src/Dodo.HttpClient.ResiliencePolicies/PoliciesHttpClientBuilderExtensions.cs +++ b/src/Dodo.HttpClient.ResiliencePolicies/PoliciesHttpClientBuilderExtensions.cs @@ -80,7 +80,7 @@ private static IAsyncPolicy BuildCircuitBreakerPolicy( return HttpPolicyExtensions .HandleTransientHttpError() .Or() - .OrResult(r => r.StatusCode == (HttpStatusCode) 429) // Too Many Requests + .OrResult(settings.AdditionalFailureResultFilter) .AdvancedCircuitBreakerAsync(settings); } diff --git a/src/Dodo.HttpClient.ResiliencePolicies/ResiliencePoliciesSettings.cs b/src/Dodo.HttpClient.ResiliencePolicies/ResiliencePoliciesSettings.cs index 29afa2a..91c7edf 100644 --- a/src/Dodo.HttpClient.ResiliencePolicies/ResiliencePoliciesSettings.cs +++ b/src/Dodo.HttpClient.ResiliencePolicies/ResiliencePoliciesSettings.cs @@ -44,11 +44,13 @@ public CircuitBreakerPolicySettings CircuitBreakerPolicySettings var onBreakHandler = OnBreak; var onResetHandler = OnReset; var onHalfOpenHandler = OnHalfOpen; + var additionalFailureResultFilter = AdditionalFailureResultFilter; _circuitBreakerPolicySettings = value; _circuitBreakerPolicySettings.OnBreak = onBreakHandler; _circuitBreakerPolicySettings.OnReset = onResetHandler; _circuitBreakerPolicySettings.OnHalfOpen = onHalfOpenHandler; + _circuitBreakerPolicySettings.AdditionalFailureResultFilter = additionalFailureResultFilter; } } @@ -75,5 +77,11 @@ public Action OnHalfOpen get => CircuitBreakerPolicySettings.OnHalfOpen; set => CircuitBreakerPolicySettings.OnHalfOpen = value; } + + public Func AdditionalFailureResultFilter + { + get => CircuitBreakerPolicySettings.AdditionalFailureResultFilter; + set => CircuitBreakerPolicySettings.AdditionalFailureResultFilter = value; + } } } From 34b944071502dd3fd8792fc3a8aee348a98046f3 Mon Sep 17 00:00:00 2001 From: Evgeniy Drenkov Date: Tue, 4 Apr 2023 16:03:41 +0300 Subject: [PATCH 2/6] Bump version --- .../Dodo.HttpClient.ResiliencePolicies.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dodo.HttpClient.ResiliencePolicies/Dodo.HttpClient.ResiliencePolicies.csproj b/src/Dodo.HttpClient.ResiliencePolicies/Dodo.HttpClient.ResiliencePolicies.csproj index 8921d63..c011efd 100644 --- a/src/Dodo.HttpClient.ResiliencePolicies/Dodo.HttpClient.ResiliencePolicies.csproj +++ b/src/Dodo.HttpClient.ResiliencePolicies/Dodo.HttpClient.ResiliencePolicies.csproj @@ -6,7 +6,7 @@ net5.0 netcoreapp3.1 8.0 - 2.0.2 + 2.0.3 Dodo.HttpClient.ResiliencePolicies Dodo.HttpClientResiliencePolicies true From 73748ba0435fa3b411c5b21f30eda4617ea142b5 Mon Sep 17 00:00:00 2001 From: Evgeniy Drenkov Date: Tue, 4 Apr 2023 16:58:03 +0300 Subject: [PATCH 3/6] Added new setting to doc --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 45f12cc..4a613b6 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,7 @@ Also you may check the [defaults](src/Dodo.HttpClient.ResiliencePolicies/Default OnBreak = (response, time) => { ... }, // Handle CircuitBreaker break event. For example you may add logging here OnReset = () => {...}, // Handle CircuitBreaker reset event. For example you may add logging here OnHalfOpen = () => {...}, // Handle CircuitBreaker reset event. For example you may add logging here + AdditionalFailureResultFilter = r => false // Additional condition for CircuitBreaker to open (opens on TooManyRequests by default) } ``` From e052ae85b764871f142d256acdbd778bf9a16d97 Mon Sep 17 00:00:00 2001 From: Evgeniy Drenkov Date: Wed, 5 Apr 2023 13:23:38 +0300 Subject: [PATCH 4/6] Changes proposed after code review --- README.md | 10 +++++----- .../CircuitBreakerPolicyTests.cs | 2 +- .../CircuitBreakerPolicy/BreakConditions.cs | 13 +++++++++++++ .../CircuitBreakerPolicySettings.cs | 6 ++---- .../PoliciesHttpClientBuilderExtensions.cs | 2 +- .../ResiliencePoliciesSettings.cs | 10 +++++----- 6 files changed, 27 insertions(+), 16 deletions(-) create mode 100644 src/Dodo.HttpClient.ResiliencePolicies/CircuitBreakerPolicy/BreakConditions.cs diff --git a/README.md b/README.md index 4a613b6..0df7df2 100644 --- a/README.md +++ b/README.md @@ -93,11 +93,11 @@ Also you may check the [defaults](src/Dodo.HttpClient.ResiliencePolicies/Default durationOfBreak: TimeSpan.FromSeconds(5), samplingDuration: TimeSpan.FromSeconds(30) ), - OnRetry = (response, time) => { ... }, // Handle retry event. For example you may add logging here - OnBreak = (response, time) => { ... }, // Handle CircuitBreaker break event. For example you may add logging here - OnReset = () => {...}, // Handle CircuitBreaker reset event. For example you may add logging here - OnHalfOpen = () => {...}, // Handle CircuitBreaker reset event. For example you may add logging here - AdditionalFailureResultFilter = r => false // Additional condition for CircuitBreaker to open (opens on TooManyRequests by default) + OnRetry = (response, time) => { ... }, // Handle retry event. For example you may add logging here + OnBreak = (response, time) => { ... }, // Handle CircuitBreaker break event. For example you may add logging here + OnReset = () => {...}, // Handle CircuitBreaker reset event. For example you may add logging here + OnHalfOpen = () => {...}, // Handle CircuitBreaker reset event. For example you may add logging here + ExtraBreakCondition = BreakConditions.OnTooManyRequests // Extra condition for CircuitBreaker to open (opens on TooManyRequests by default) } ``` diff --git a/src/Dodo.HttpClient.ResiliencePolicies.Tests/CircuitBreakerPolicyTests.cs b/src/Dodo.HttpClient.ResiliencePolicies.Tests/CircuitBreakerPolicyTests.cs index 0688cc8..f46e808 100644 --- a/src/Dodo.HttpClient.ResiliencePolicies.Tests/CircuitBreakerPolicyTests.cs +++ b/src/Dodo.HttpClient.ResiliencePolicies.Tests/CircuitBreakerPolicyTests.cs @@ -73,7 +73,7 @@ public void Should_not_break_on_429() OverallTimeout = TimeSpan.FromSeconds(5), RetryPolicySettings = RetryPolicySettings.Constant(retryCount, TimeSpan.FromMilliseconds(100)), CircuitBreakerPolicySettings = BuildCircuitBreakerSettings(minimumThroughput), - AdditionalFailureResultFilter = _ => false, // no additional filter (default is true for 429) + ExtraBreakCondition = BreakConditions.None }; var wrapper = Create.HttpClientWrapperWrapperBuilder .WithStatusCode(HttpStatusCode.TooManyRequests) diff --git a/src/Dodo.HttpClient.ResiliencePolicies/CircuitBreakerPolicy/BreakConditions.cs b/src/Dodo.HttpClient.ResiliencePolicies/CircuitBreakerPolicy/BreakConditions.cs new file mode 100644 index 0000000..83add0e --- /dev/null +++ b/src/Dodo.HttpClient.ResiliencePolicies/CircuitBreakerPolicy/BreakConditions.cs @@ -0,0 +1,13 @@ +using System; +using System.Net; +using System.Net.Http; + +namespace Dodo.HttpClientResiliencePolicies.CircuitBreakerPolicy +{ + public static class BreakConditions + { + public static readonly Func OnTooManyRequests = response => + response.StatusCode == (HttpStatusCode)429; // Too Many Requests + public static readonly Func None = _ => false; + } +} diff --git a/src/Dodo.HttpClient.ResiliencePolicies/CircuitBreakerPolicy/CircuitBreakerPolicySettings.cs b/src/Dodo.HttpClient.ResiliencePolicies/CircuitBreakerPolicy/CircuitBreakerPolicySettings.cs index 827ecce..23e836b 100644 --- a/src/Dodo.HttpClient.ResiliencePolicies/CircuitBreakerPolicy/CircuitBreakerPolicySettings.cs +++ b/src/Dodo.HttpClient.ResiliencePolicies/CircuitBreakerPolicy/CircuitBreakerPolicySettings.cs @@ -15,7 +15,7 @@ public sealed class CircuitBreakerPolicySettings internal Action, TimeSpan> OnBreak { get; set; } internal Action OnReset { get; set; } internal Action OnHalfOpen { get; set; } - internal Func AdditionalFailureResultFilter { get; set; } + internal Func ExtraBreakCondition { get; set; } public CircuitBreakerPolicySettings() : this( @@ -40,13 +40,11 @@ public CircuitBreakerPolicySettings( OnBreak = DoNothingOnBreak; OnReset = DoNothingOnReset; OnHalfOpen = DoNothingOnHalfOpen; - AdditionalFailureResultFilter = HandleTooManyRequests; + ExtraBreakCondition = BreakConditions.OnTooManyRequests; } private static readonly Action, TimeSpan> DoNothingOnBreak = (_, __) => { }; private static readonly Action DoNothingOnReset = () => { }; private static readonly Action DoNothingOnHalfOpen = () => { }; - private static readonly Func HandleTooManyRequests = response => - response.StatusCode == (HttpStatusCode)429; // Too Many Requests } } diff --git a/src/Dodo.HttpClient.ResiliencePolicies/PoliciesHttpClientBuilderExtensions.cs b/src/Dodo.HttpClient.ResiliencePolicies/PoliciesHttpClientBuilderExtensions.cs index 45e45cd..7e05d1b 100644 --- a/src/Dodo.HttpClient.ResiliencePolicies/PoliciesHttpClientBuilderExtensions.cs +++ b/src/Dodo.HttpClient.ResiliencePolicies/PoliciesHttpClientBuilderExtensions.cs @@ -80,7 +80,7 @@ private static IAsyncPolicy BuildCircuitBreakerPolicy( return HttpPolicyExtensions .HandleTransientHttpError() .Or() - .OrResult(settings.AdditionalFailureResultFilter) + .OrResult(settings.ExtraBreakCondition) .AdvancedCircuitBreakerAsync(settings); } diff --git a/src/Dodo.HttpClient.ResiliencePolicies/ResiliencePoliciesSettings.cs b/src/Dodo.HttpClient.ResiliencePolicies/ResiliencePoliciesSettings.cs index 91c7edf..fb664ec 100644 --- a/src/Dodo.HttpClient.ResiliencePolicies/ResiliencePoliciesSettings.cs +++ b/src/Dodo.HttpClient.ResiliencePolicies/ResiliencePoliciesSettings.cs @@ -44,13 +44,13 @@ public CircuitBreakerPolicySettings CircuitBreakerPolicySettings var onBreakHandler = OnBreak; var onResetHandler = OnReset; var onHalfOpenHandler = OnHalfOpen; - var additionalFailureResultFilter = AdditionalFailureResultFilter; + var extraBreakCondition = ExtraBreakCondition; _circuitBreakerPolicySettings = value; _circuitBreakerPolicySettings.OnBreak = onBreakHandler; _circuitBreakerPolicySettings.OnReset = onResetHandler; _circuitBreakerPolicySettings.OnHalfOpen = onHalfOpenHandler; - _circuitBreakerPolicySettings.AdditionalFailureResultFilter = additionalFailureResultFilter; + _circuitBreakerPolicySettings.ExtraBreakCondition = extraBreakCondition; } } @@ -78,10 +78,10 @@ public Action OnHalfOpen set => CircuitBreakerPolicySettings.OnHalfOpen = value; } - public Func AdditionalFailureResultFilter + public Func ExtraBreakCondition { - get => CircuitBreakerPolicySettings.AdditionalFailureResultFilter; - set => CircuitBreakerPolicySettings.AdditionalFailureResultFilter = value; + get => CircuitBreakerPolicySettings.ExtraBreakCondition; + set => CircuitBreakerPolicySettings.ExtraBreakCondition = value; } } } From 4a5081c18e87de026c1fe7e43a3153bcaaba2a2a Mon Sep 17 00:00:00 2001 From: Evgeniy Drenkov Date: Wed, 5 Apr 2023 13:25:20 +0300 Subject: [PATCH 5/6] Removed some usings --- .../CircuitBreakerPolicy/CircuitBreakerPolicySettings.cs | 1 - .../PoliciesHttpClientBuilderExtensions.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Dodo.HttpClient.ResiliencePolicies/CircuitBreakerPolicy/CircuitBreakerPolicySettings.cs b/src/Dodo.HttpClient.ResiliencePolicies/CircuitBreakerPolicy/CircuitBreakerPolicySettings.cs index 23e836b..2e9e7d0 100644 --- a/src/Dodo.HttpClient.ResiliencePolicies/CircuitBreakerPolicy/CircuitBreakerPolicySettings.cs +++ b/src/Dodo.HttpClient.ResiliencePolicies/CircuitBreakerPolicy/CircuitBreakerPolicySettings.cs @@ -1,5 +1,4 @@ using System; -using System.Net; using System.Net.Http; using Polly; diff --git a/src/Dodo.HttpClient.ResiliencePolicies/PoliciesHttpClientBuilderExtensions.cs b/src/Dodo.HttpClient.ResiliencePolicies/PoliciesHttpClientBuilderExtensions.cs index 7e05d1b..598e5c5 100644 --- a/src/Dodo.HttpClient.ResiliencePolicies/PoliciesHttpClientBuilderExtensions.cs +++ b/src/Dodo.HttpClient.ResiliencePolicies/PoliciesHttpClientBuilderExtensions.cs @@ -1,5 +1,4 @@ using System; -using System.Net; using System.Net.Http; using Dodo.HttpClientResiliencePolicies.CircuitBreakerPolicy; using Dodo.HttpClientResiliencePolicies.RetryPolicy; From 84416cd2222af977f3e844536f2209c3becb4872 Mon Sep 17 00:00:00 2001 From: Evgeniy Drenkov Date: Wed, 5 Apr 2023 13:27:15 +0300 Subject: [PATCH 6/6] Bump version --- .../Dodo.HttpClient.ResiliencePolicies.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dodo.HttpClient.ResiliencePolicies/Dodo.HttpClient.ResiliencePolicies.csproj b/src/Dodo.HttpClient.ResiliencePolicies/Dodo.HttpClient.ResiliencePolicies.csproj index c011efd..7152d86 100644 --- a/src/Dodo.HttpClient.ResiliencePolicies/Dodo.HttpClient.ResiliencePolicies.csproj +++ b/src/Dodo.HttpClient.ResiliencePolicies/Dodo.HttpClient.ResiliencePolicies.csproj @@ -6,7 +6,7 @@ net5.0 netcoreapp3.1 8.0 - 2.0.3 + 2.1.0 Dodo.HttpClient.ResiliencePolicies Dodo.HttpClientResiliencePolicies true