From c8e0366da89afcfe5d268fbf7f709b51f966235e Mon Sep 17 00:00:00 2001 From: martintmk <103487740+martintmk@users.noreply.github.com> Date: Thu, 10 Aug 2023 10:23:19 +0200 Subject: [PATCH 1/8] Add new metering tests to cover uncovered lines (#1474) --- .../ResilienceTelemetryDiagnosticSourceTests.cs | 16 +++++++++++++++- test/Polly.TestUtils/TestUtilities.cs | 7 ++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/test/Polly.Extensions.Tests/Telemetry/ResilienceTelemetryDiagnosticSourceTests.cs b/test/Polly.Extensions.Tests/Telemetry/ResilienceTelemetryDiagnosticSourceTests.cs index dcb3c4dd73c..1268e5c3925 100644 --- a/test/Polly.Extensions.Tests/Telemetry/ResilienceTelemetryDiagnosticSourceTests.cs +++ b/test/Polly.Extensions.Tests/Telemetry/ResilienceTelemetryDiagnosticSourceTests.cs @@ -12,8 +12,8 @@ public class ResilienceTelemetryDiagnosticSourceTests : IDisposable private readonly FakeLogger _logger; private readonly ILoggerFactory _loggerFactory; private readonly List _events = new(); - private readonly IDisposable _metering; private Action? _onEvent; + private IDisposable _metering; public ResilienceTelemetryDiagnosticSourceTests() { @@ -290,6 +290,20 @@ public void WriteExecutionAttemptEvent_Metering_Ok(bool noOutcome, bool exceptio _events.Single(v => v.Name == "execution-attempt-duration").Measurement.Should().Be(50000); } + [Fact] + public void WriteExecutionAttemptEvent_ShouldBeSkipped() + { + _metering.Dispose(); + _metering = TestUtilities.EnablePollyMetering(_events, _ => false); + + var telemetry = Create(); + var attemptArg = new ExecutionAttemptArguments(5, TimeSpan.FromSeconds(50), true); + ReportEvent(telemetry, Outcome.FromResult(true), context: ResilienceContextPool.Shared.Get("op-key").WithResultType(), arg: attemptArg); + + var events = GetEvents("execution-attempt-duration"); + events.Should().HaveCount(0); + } + [InlineData(1)] [InlineData(100)] [Theory] diff --git a/test/Polly.TestUtils/TestUtilities.cs b/test/Polly.TestUtils/TestUtilities.cs index 2e8d783958e..1ea4cdd3ca1 100644 --- a/test/Polly.TestUtils/TestUtilities.cs +++ b/test/Polly.TestUtils/TestUtilities.cs @@ -52,7 +52,7 @@ public static ILoggerFactory CreateLoggerFactory(out FakeLogger logger) return loggerFactory; } - public static IDisposable EnablePollyMetering(ICollection events) + public static IDisposable EnablePollyMetering(ICollection events, Predicate? shouldListen = null) { var stateStr = Guid.NewGuid().ToString(); var meterListener = new MeterListener @@ -61,6 +61,11 @@ public static IDisposable EnablePollyMetering(ICollection events) { if (instrument.Meter.Name == "Polly") { + if (shouldListen is not null && !shouldListen(instrument)) + { + return; + } + listener.EnableMeasurementEvents(instrument, stateStr); } } From 5f253dcca85173f36f924feec5834e0246fb99ab Mon Sep 17 00:00:00 2001 From: martintmk <103487740+martintmk@users.noreply.github.com> Date: Thu, 10 Aug 2023 10:57:07 +0200 Subject: [PATCH 2/8] Default names for individual resilience strategies (#1475) --- .../CircuitBreaker/CircuitBreakerConstants.cs | 2 ++ .../CircuitBreakerStrategyOptions.TResult.cs | 5 ++++ src/Polly.Core/Fallback/FallbackConstants.cs | 2 ++ .../FallbackStrategyOptions.TResult.cs | 5 ++++ src/Polly.Core/Hedging/HedgingConstants.cs | 2 ++ .../Hedging/HedgingStrategyOptions.TResult.cs | 5 ++++ src/Polly.Core/Retry/RetryConstants.cs | 2 ++ .../Retry/RetryStrategyOptions.TResult.cs | 5 ++++ src/Polly.Core/Timeout/TimeoutConstants.cs | 2 ++ .../Timeout/TimeoutStrategyOptions.cs | 5 ++++ .../RateLimiterConstants.cs | 2 ++ .../RateLimiterStrategyOptions.cs | 5 ++++ .../CircuitBreakerOptionsTests.cs | 27 ++----------------- .../Fallback/FallbackStrategyOptionsTests.cs | 1 + .../Hedging/HedgingStrategyOptionsTests.cs | 1 + .../Retry/RetryStrategyOptionsTests.cs | 1 + .../Timeout/TimeoutStrategyOptionsTests.cs | 1 + .../RateLimiterStrategyOptionsTests.cs | 1 + 18 files changed, 49 insertions(+), 25 deletions(-) diff --git a/src/Polly.Core/CircuitBreaker/CircuitBreakerConstants.cs b/src/Polly.Core/CircuitBreaker/CircuitBreakerConstants.cs index 6b31f0794cd..cf4adc1457c 100644 --- a/src/Polly.Core/CircuitBreaker/CircuitBreakerConstants.cs +++ b/src/Polly.Core/CircuitBreaker/CircuitBreakerConstants.cs @@ -2,6 +2,8 @@ namespace Polly.CircuitBreaker; internal static class CircuitBreakerConstants { + public const string DefaultName = "CircuitBreaker"; + public const string OnCircuitClosed = "OnCircuitClosed"; public const string OnHalfOpenEvent = "OnCircuitHalfOpened"; diff --git a/src/Polly.Core/CircuitBreaker/CircuitBreakerStrategyOptions.TResult.cs b/src/Polly.Core/CircuitBreaker/CircuitBreakerStrategyOptions.TResult.cs index 7f371beff8a..42e47cf7de2 100644 --- a/src/Polly.Core/CircuitBreaker/CircuitBreakerStrategyOptions.TResult.cs +++ b/src/Polly.Core/CircuitBreaker/CircuitBreakerStrategyOptions.TResult.cs @@ -22,6 +22,11 @@ namespace Polly.CircuitBreaker; /// public class CircuitBreakerStrategyOptions : ResilienceStrategyOptions { + /// + /// Initializes a new instance of the class. + /// + public CircuitBreakerStrategyOptions() => Name = CircuitBreakerConstants.DefaultName; + /// /// Gets or sets the failure-to-success ratio at which the circuit will break. /// diff --git a/src/Polly.Core/Fallback/FallbackConstants.cs b/src/Polly.Core/Fallback/FallbackConstants.cs index bc5913cf7a9..de2a9609748 100644 --- a/src/Polly.Core/Fallback/FallbackConstants.cs +++ b/src/Polly.Core/Fallback/FallbackConstants.cs @@ -2,6 +2,8 @@ namespace Polly.Fallback; internal static class FallbackConstants { + public const string DefaultName = "Fallback"; + public const string OnFallback = "OnFallback"; } diff --git a/src/Polly.Core/Fallback/FallbackStrategyOptions.TResult.cs b/src/Polly.Core/Fallback/FallbackStrategyOptions.TResult.cs index 1f4e55420d4..687f4e85ac9 100644 --- a/src/Polly.Core/Fallback/FallbackStrategyOptions.TResult.cs +++ b/src/Polly.Core/Fallback/FallbackStrategyOptions.TResult.cs @@ -8,6 +8,11 @@ namespace Polly.Fallback; /// The result type. public class FallbackStrategyOptions : ResilienceStrategyOptions { + /// + /// Initializes a new instance of the class. + /// + public FallbackStrategyOptions() => Name = FallbackConstants.DefaultName; + /// /// Gets or sets the outcome predicate for determining whether a fallback should be executed. /// diff --git a/src/Polly.Core/Hedging/HedgingConstants.cs b/src/Polly.Core/Hedging/HedgingConstants.cs index adcd2134831..d6b1bb9f1d0 100644 --- a/src/Polly.Core/Hedging/HedgingConstants.cs +++ b/src/Polly.Core/Hedging/HedgingConstants.cs @@ -2,6 +2,8 @@ namespace Polly.Hedging; internal static class HedgingConstants { + public const string DefaultName = "Hedging"; + public const string OnHedgingEventName = "OnHedging"; public const int DefaultMaxHedgedAttempts = 2; diff --git a/src/Polly.Core/Hedging/HedgingStrategyOptions.TResult.cs b/src/Polly.Core/Hedging/HedgingStrategyOptions.TResult.cs index 49a543a604e..45d80b94ecc 100644 --- a/src/Polly.Core/Hedging/HedgingStrategyOptions.TResult.cs +++ b/src/Polly.Core/Hedging/HedgingStrategyOptions.TResult.cs @@ -8,6 +8,11 @@ namespace Polly.Hedging; /// The type of result these hedging options handle. public class HedgingStrategyOptions : ResilienceStrategyOptions { + /// + /// Initializes a new instance of the class. + /// + public HedgingStrategyOptions() => Name = HedgingConstants.DefaultName; + /// /// Gets or sets the minimal time of waiting before spawning a new hedged call. /// diff --git a/src/Polly.Core/Retry/RetryConstants.cs b/src/Polly.Core/Retry/RetryConstants.cs index d45a3d12ed8..a4da3a579b3 100644 --- a/src/Polly.Core/Retry/RetryConstants.cs +++ b/src/Polly.Core/Retry/RetryConstants.cs @@ -2,6 +2,8 @@ namespace Polly.Retry; internal static class RetryConstants { + public const string DefaultName = "Retry"; + public const string OnRetryEvent = "OnRetry"; public const RetryBackoffType DefaultBackoffType = RetryBackoffType.Constant; diff --git a/src/Polly.Core/Retry/RetryStrategyOptions.TResult.cs b/src/Polly.Core/Retry/RetryStrategyOptions.TResult.cs index fdd678d3f50..ad8cdd19f88 100644 --- a/src/Polly.Core/Retry/RetryStrategyOptions.TResult.cs +++ b/src/Polly.Core/Retry/RetryStrategyOptions.TResult.cs @@ -8,6 +8,11 @@ namespace Polly.Retry; /// The type of result the retry strategy handles. public class RetryStrategyOptions : ResilienceStrategyOptions { + /// + /// Initializes a new instance of the class. + /// + public RetryStrategyOptions() => Name = RetryConstants.DefaultName; + /// /// Gets or sets the maximum number of retries to use, in addition to the original call. /// diff --git a/src/Polly.Core/Timeout/TimeoutConstants.cs b/src/Polly.Core/Timeout/TimeoutConstants.cs index 097c654827d..fa6437721b1 100644 --- a/src/Polly.Core/Timeout/TimeoutConstants.cs +++ b/src/Polly.Core/Timeout/TimeoutConstants.cs @@ -2,5 +2,7 @@ namespace Polly.Timeout; internal static class TimeoutConstants { + public const string DefaultName = "Timeout"; + public const string OnTimeoutEvent = "OnTimeout"; } diff --git a/src/Polly.Core/Timeout/TimeoutStrategyOptions.cs b/src/Polly.Core/Timeout/TimeoutStrategyOptions.cs index 3b65b478efa..3edcc597a87 100644 --- a/src/Polly.Core/Timeout/TimeoutStrategyOptions.cs +++ b/src/Polly.Core/Timeout/TimeoutStrategyOptions.cs @@ -7,6 +7,11 @@ namespace Polly.Timeout; /// public class TimeoutStrategyOptions : ResilienceStrategyOptions { + /// + /// Initializes a new instance of the class. + /// + public TimeoutStrategyOptions() => Name = TimeoutConstants.DefaultName; + #pragma warning disable IL2026 // Addressed with DynamicDependency on ValidationHelper.Validate method /// /// Gets or sets the default timeout. diff --git a/src/Polly.RateLimiting/RateLimiterConstants.cs b/src/Polly.RateLimiting/RateLimiterConstants.cs index f56eb43f674..5b0c205f48b 100644 --- a/src/Polly.RateLimiting/RateLimiterConstants.cs +++ b/src/Polly.RateLimiting/RateLimiterConstants.cs @@ -2,6 +2,8 @@ namespace Polly.RateLimiting; internal static class RateLimiterConstants { + public const string DefaultName = "RateLimiter"; + public const int DefaultPermitLimit = 1000; public const int DefaultQueueLimit = 0; diff --git a/src/Polly.RateLimiting/RateLimiterStrategyOptions.cs b/src/Polly.RateLimiting/RateLimiterStrategyOptions.cs index 1eac041ee44..e64ffb49702 100644 --- a/src/Polly.RateLimiting/RateLimiterStrategyOptions.cs +++ b/src/Polly.RateLimiting/RateLimiterStrategyOptions.cs @@ -8,6 +8,11 @@ namespace Polly.RateLimiting; /// public class RateLimiterStrategyOptions : ResilienceStrategyOptions { + /// + /// Initializes a new instance of the class. + /// + public RateLimiterStrategyOptions() => Name = RateLimiterConstants.DefaultName; + /// /// Gets or sets the default rate limiter options. /// diff --git a/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerOptionsTests.cs b/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerOptionsTests.cs index 6473dbacf95..10a8127d807 100644 --- a/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerOptionsTests.cs +++ b/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerOptionsTests.cs @@ -6,29 +6,6 @@ namespace Polly.Core.Tests.CircuitBreaker; public class CircuitBreakerOptionsTests { - [Fact] - public void Ctor_Defaults() - { - var options = new CircuitBreakerStrategyOptions(); - options.BreakDuration.Should().Be(TimeSpan.FromSeconds(5)); - options.FailureRatio.Should().Be(0.1); - options.MinimumThroughput.Should().Be(100); - options.SamplingDuration.Should().Be(TimeSpan.FromSeconds(30)); - options.OnOpened.Should().BeNull(); - options.OnClosed.Should().BeNull(); - options.OnHalfOpened.Should().BeNull(); - options.ShouldHandle.Should().NotBeNull(); - options.Name.Should().BeNull(); - - // now set to min values - options.FailureRatio = 0.001; - options.BreakDuration = TimeSpan.FromMilliseconds(500); - options.MinimumThroughput = 2; - options.SamplingDuration = TimeSpan.FromMilliseconds(500); - - ValidationHelper.ValidateObject(new(options, "Dummy.")); - } - [Fact] public async Task ShouldHandle_EnsureDefaults() { @@ -42,7 +19,7 @@ public async Task ShouldHandle_EnsureDefaults() } [Fact] - public void Ctor_Generic_Defaults() + public void Ctor_Defaults() { var options = new CircuitBreakerStrategyOptions(); @@ -54,7 +31,7 @@ public void Ctor_Generic_Defaults() options.OnClosed.Should().BeNull(); options.OnHalfOpened.Should().BeNull(); options.ShouldHandle.Should().NotBeNull(); - options.Name.Should().BeNull(); + options.Name.Should().Be("CircuitBreaker"); // now set to min values options.FailureRatio = 0.001; diff --git a/test/Polly.Core.Tests/Fallback/FallbackStrategyOptionsTests.cs b/test/Polly.Core.Tests/Fallback/FallbackStrategyOptionsTests.cs index 430e03abce9..5417bae9c95 100644 --- a/test/Polly.Core.Tests/Fallback/FallbackStrategyOptionsTests.cs +++ b/test/Polly.Core.Tests/Fallback/FallbackStrategyOptionsTests.cs @@ -14,6 +14,7 @@ public void Ctor_EnsureDefaults() options.ShouldHandle.Should().NotBeNull(); options.OnFallback.Should().BeNull(); options.FallbackAction.Should().BeNull(); + options.Name.Should().Be("Fallback"); } [Fact] diff --git a/test/Polly.Core.Tests/Hedging/HedgingStrategyOptionsTests.cs b/test/Polly.Core.Tests/Hedging/HedgingStrategyOptionsTests.cs index cdf470a53ba..4d2a55e72ff 100644 --- a/test/Polly.Core.Tests/Hedging/HedgingStrategyOptionsTests.cs +++ b/test/Polly.Core.Tests/Hedging/HedgingStrategyOptionsTests.cs @@ -16,6 +16,7 @@ public void Ctor_EnsureDefaults() options.HedgingDelay.Should().Be(TimeSpan.FromSeconds(2)); options.MaxHedgedAttempts.Should().Be(2); options.OnHedging.Should().BeNull(); + options.Name.Should().Be("Hedging"); } [InlineData(true)] diff --git a/test/Polly.Core.Tests/Retry/RetryStrategyOptionsTests.cs b/test/Polly.Core.Tests/Retry/RetryStrategyOptionsTests.cs index f7d8c47e060..85be2996f9b 100644 --- a/test/Polly.Core.Tests/Retry/RetryStrategyOptionsTests.cs +++ b/test/Polly.Core.Tests/Retry/RetryStrategyOptionsTests.cs @@ -20,6 +20,7 @@ public void Ctor_Ok() options.RetryCount.Should().Be(3); options.BackoffType.Should().Be(RetryBackoffType.Constant); options.BaseDelay.Should().Be(TimeSpan.FromSeconds(2)); + options.Name.Should().Be("Retry"); } [Fact] diff --git a/test/Polly.Core.Tests/Timeout/TimeoutStrategyOptionsTests.cs b/test/Polly.Core.Tests/Timeout/TimeoutStrategyOptionsTests.cs index 9a83f874218..c825b76a272 100644 --- a/test/Polly.Core.Tests/Timeout/TimeoutStrategyOptionsTests.cs +++ b/test/Polly.Core.Tests/Timeout/TimeoutStrategyOptionsTests.cs @@ -13,6 +13,7 @@ public void Ctor_EnsureDefaultValues() options.TimeoutGenerator.Should().BeNull(); options.OnTimeout.Should().BeNull(); + options.Name.Should().Be("Timeout"); } [MemberData(nameof(TimeoutTestUtils.InvalidTimeouts), MemberType = typeof(TimeoutTestUtils))] diff --git a/test/Polly.RateLimiting.Tests/RateLimiterStrategyOptionsTests.cs b/test/Polly.RateLimiting.Tests/RateLimiterStrategyOptionsTests.cs index cfdb9782271..23fba9dc63c 100644 --- a/test/Polly.RateLimiting.Tests/RateLimiterStrategyOptionsTests.cs +++ b/test/Polly.RateLimiting.Tests/RateLimiterStrategyOptionsTests.cs @@ -11,5 +11,6 @@ public void Ctor_EnsureDefaults() options.OnRejected.Should().BeNull(); options.DefaultRateLimiterOptions.PermitLimit.Should().Be(1000); options.DefaultRateLimiterOptions.QueueLimit.Should().Be(0); + options.Name.Should().Be("RateLimiter"); } } From 36635100576089fee81177ba30c150c7ad14d44f Mon Sep 17 00:00:00 2001 From: martintmk <103487740+martintmk@users.noreply.github.com> Date: Thu, 10 Aug 2023 16:12:42 +0200 Subject: [PATCH 3/8] Introduce `NonReactiveResilienceStrategy` (#1476) --- .../TelemetryBenchmark.cs | 2 +- .../Utils/EmptyResilienceStrategy.cs | 2 +- .../Utils/Helper.CircuitBreaker.cs | 4 +- .../Utils/Helper.StrategyPipeline.cs | 2 +- .../CompositeStrategyBuilderExtensions.cs | 7 +- .../NonReactiveResilienceStrategy.cs | 36 ++++++++++ src/Polly.Core/NullResilienceStrategy.cs | 2 +- src/Polly.Core/PublicAPI.Unshipped.txt | 9 +-- src/Polly.Core/ReactiveResilienceStrategy.cs | 2 +- src/Polly.Core/ResilienceStrategy.cs | 34 ++------- .../Timeout/TimeoutResilienceStrategy.cs | 2 +- .../Utils/CompositeResilienceStrategy.cs | 4 +- .../NonReactiveResilienceStrategyBridge.cs | 14 ++++ .../Utils/ReactiveResilienceStrategyBridge.cs | 2 +- .../Utils/ReloadableResilienceStrategy.cs | 2 +- ...metryCompositeStrategyBuilderExtensions.cs | 9 ++- .../Telemetry/TelemetryResilienceStrategy.cs | 2 +- .../RateLimiterResilienceStrategy.cs | 2 +- .../ResilienceStrategyExtensions.cs | 9 ++- .../CompositeStrategyBuilderTests.cs | 38 +++++----- .../GenericCompositeStrategyBuilderTests.cs | 2 +- .../ResilienceStrategyProviderTests.cs | 2 +- .../ResilienceStrategyRegistryTests.cs | 21 +++--- .../ResilienceStrategyTTests.Async.cs | 2 +- .../ResilienceStrategyTTests.Sync.cs | 2 +- .../ResilienceStrategyTests.Async.cs | 4 +- .../ResilienceStrategyTests.AsyncT.cs | 4 +- .../ResilienceStrategyTests.Sync.cs | 4 +- .../ResilienceStrategyTests.SyncT.cs | 4 +- .../ResilienceStrategyTests.cs | 4 +- ...CompositeStrategyBuilderExtensionsTests.cs | 6 +- .../Timeout/TimeoutResilienceStrategyTests.cs | 2 +- .../Utils/CompositeResilienceStrategyTests.cs | 38 +++++----- .../ReloadableResilienceStrategyTests.cs | 10 +-- .../PollyServiceCollectionExtensionTests.cs | 2 +- ...s.OnCircuitBreakWithServiceProvider_796.cs | 2 +- .../ReloadableResilienceStrategyTests.cs | 2 +- .../TelemetryResilienceStrategyTests.cs | 10 ++- .../Polly.RateLimiting.Tests.csproj | 1 + ...CompositeStrategyBuilderExtensionsTests.cs | 69 ++++++++----------- .../RateLimiterResilienceStrategyTests.cs | 15 ++-- ...lienceStrategyConversionExtensionsTests.cs | 8 +-- .../NonReactiveStrategyExtensions.cs | 14 ++++ .../TestResilienceStrategy.TResult.cs | 2 +- .../Polly.TestUtils/TestResilienceStrategy.cs | 2 +- .../ResilienceStrategyExtensionsTests.cs | 12 ++-- 46 files changed, 243 insertions(+), 185 deletions(-) create mode 100644 src/Polly.Core/NonReactiveResilienceStrategy.cs create mode 100644 src/Polly.Core/Utils/NonReactiveResilienceStrategyBridge.cs create mode 100644 test/Polly.TestUtils/NonReactiveStrategyExtensions.cs diff --git a/bench/Polly.Core.Benchmarks/TelemetryBenchmark.cs b/bench/Polly.Core.Benchmarks/TelemetryBenchmark.cs index f62e05293d2..b98f59d91c1 100644 --- a/bench/Polly.Core.Benchmarks/TelemetryBenchmark.cs +++ b/bench/Polly.Core.Benchmarks/TelemetryBenchmark.cs @@ -66,7 +66,7 @@ private ResilienceStrategy Build(CompositeStrategyBuilder builder) return builder.Build(); } - private class TelemetryEventStrategy : ResilienceStrategy + private class TelemetryEventStrategy : NonReactiveResilienceStrategy { private readonly ResilienceStrategyTelemetry _telemetry; diff --git a/bench/Polly.Core.Benchmarks/Utils/EmptyResilienceStrategy.cs b/bench/Polly.Core.Benchmarks/Utils/EmptyResilienceStrategy.cs index 69c4c9df0ad..3ae5f25b972 100644 --- a/bench/Polly.Core.Benchmarks/Utils/EmptyResilienceStrategy.cs +++ b/bench/Polly.Core.Benchmarks/Utils/EmptyResilienceStrategy.cs @@ -1,6 +1,6 @@ namespace Polly.Core.Benchmarks.Utils; -internal class EmptyResilienceStrategy : ResilienceStrategy +internal class EmptyResilienceStrategy : NonReactiveResilienceStrategy { protected override ValueTask> ExecuteCore( Func>> callback, diff --git a/bench/Polly.Core.Benchmarks/Utils/Helper.CircuitBreaker.cs b/bench/Polly.Core.Benchmarks/Utils/Helper.CircuitBreaker.cs index 43af5ba582b..de6d0340caf 100644 --- a/bench/Polly.Core.Benchmarks/Utils/Helper.CircuitBreaker.cs +++ b/bench/Polly.Core.Benchmarks/Utils/Helper.CircuitBreaker.cs @@ -17,7 +17,7 @@ public static object CreateOpenedCircuitBreaker(PollyVersion version, bool handl if (handleOutcome) { - builder.AddStrategy(new OutcomeHandlingStrategy()); + builder.AddStrategy(_ => new OutcomeHandlingStrategy(), new EmptyResilienceOptions()); } var strategy = builder.AddCircuitBreaker(options).Build(); @@ -64,7 +64,7 @@ public static object CreateCircuitBreaker(PollyVersion technology) }; } - private class OutcomeHandlingStrategy : ResilienceStrategy + private class OutcomeHandlingStrategy : NonReactiveResilienceStrategy { protected override async ValueTask> ExecuteCore( Func>> callback, diff --git a/bench/Polly.Core.Benchmarks/Utils/Helper.StrategyPipeline.cs b/bench/Polly.Core.Benchmarks/Utils/Helper.StrategyPipeline.cs index 8156caadd94..bc0d1b3c802 100644 --- a/bench/Polly.Core.Benchmarks/Utils/Helper.StrategyPipeline.cs +++ b/bench/Polly.Core.Benchmarks/Utils/Helper.StrategyPipeline.cs @@ -10,7 +10,7 @@ internal static partial class Helper { for (var i = 0; i < count; i++) { - builder.AddStrategy(new EmptyResilienceStrategy()); + builder.AddStrategy(_ => new EmptyResilienceStrategy(), new EmptyResilienceOptions()); } }), _ => throw new NotSupportedException() diff --git a/src/Polly.Core/CompositeStrategyBuilderExtensions.cs b/src/Polly.Core/CompositeStrategyBuilderExtensions.cs index a55625d9c6f..0417afcb0a5 100644 --- a/src/Polly.Core/CompositeStrategyBuilderExtensions.cs +++ b/src/Polly.Core/CompositeStrategyBuilderExtensions.cs @@ -27,7 +27,8 @@ public static TBuilder AddStrategy(this TBuilder builder, ResilienceSt Guard.NotNull(builder); Guard.NotNull(strategy); - return builder.AddStrategy(_ => strategy, EmptyOptions.Instance); + builder.AddStrategyCore(_ => strategy, EmptyOptions.Instance); + return builder; } /// @@ -59,14 +60,14 @@ public static CompositeStrategyBuilder AddStrategy(this Compos /// Thrown when this builder was already used to create a strategy. The builder cannot be modified after it has been used. /// Thrown when is invalid. [RequiresUnreferencedCode(Constants.OptionsValidation)] - public static TBuilder AddStrategy(this TBuilder builder, Func factory, ResilienceStrategyOptions options) + public static TBuilder AddStrategy(this TBuilder builder, Func factory, ResilienceStrategyOptions options) where TBuilder : CompositeStrategyBuilderBase { Guard.NotNull(builder); Guard.NotNull(factory); Guard.NotNull(options); - builder.AddStrategyCore(factory, options); + builder.AddStrategyCore(context => new NonReactiveResilienceStrategyBridge(factory(context)), options); return builder; } diff --git a/src/Polly.Core/NonReactiveResilienceStrategy.cs b/src/Polly.Core/NonReactiveResilienceStrategy.cs new file mode 100644 index 00000000000..110a091d7fe --- /dev/null +++ b/src/Polly.Core/NonReactiveResilienceStrategy.cs @@ -0,0 +1,36 @@ +namespace Polly; + +/// +/// Base class for all non-reactive resilience strategies. +/// +public abstract class NonReactiveResilienceStrategy +{ + /// + /// An implementation of a non-reactive resilience strategy that executes the specified . + /// + /// The type of result returned by the callback. + /// The type of state associated with the callback. + /// The user-provided callback. + /// The context associated with the callback. + /// The state associated with the callback. + /// + /// An instance of a pending for asynchronous executions or a completed task for synchronous executions. + /// + /// + /// This method is called for both synchronous and asynchronous execution flows. + /// + /// You can use to determine whether is synchronous or asynchronous. + /// This is useful when the custom strategy behaves differently in each execution flow. In general, for most strategies, the implementation + /// is the same for both execution flows. + /// See for more details. + /// + /// + /// The provided callback never throws an exception. Instead, the exception is captured and converted to an . + /// Similarly, do not throw exceptions from your strategy implementation. Instead, return an exception instance as . + /// + /// + protected internal abstract ValueTask> ExecuteCore( + Func>> callback, + ResilienceContext context, + TState state); +} diff --git a/src/Polly.Core/NullResilienceStrategy.cs b/src/Polly.Core/NullResilienceStrategy.cs index 66513428cd8..65e32bebe13 100644 --- a/src/Polly.Core/NullResilienceStrategy.cs +++ b/src/Polly.Core/NullResilienceStrategy.cs @@ -15,7 +15,7 @@ private NullResilienceStrategy() } /// - protected internal override ValueTask> ExecuteCore( + internal override ValueTask> ExecuteCore( Func>> callback, ResilienceContext context, TState state) diff --git a/src/Polly.Core/PublicAPI.Unshipped.txt b/src/Polly.Core/PublicAPI.Unshipped.txt index 742f6604a2c..8da2c7a3d08 100644 --- a/src/Polly.Core/PublicAPI.Unshipped.txt +++ b/src/Polly.Core/PublicAPI.Unshipped.txt @@ -1,9 +1,9 @@ -abstract Polly.ReactiveResilienceStrategy.ExecuteCore(System.Func>>! callback, Polly.ResilienceContext! context, TState state) -> System.Threading.Tasks.ValueTask> +abstract Polly.NonReactiveResilienceStrategy.ExecuteCore(System.Func>>! callback, Polly.ResilienceContext! context, TState state) -> System.Threading.Tasks.ValueTask> +abstract Polly.ReactiveResilienceStrategy.ExecuteCore(System.Func>>! callback, Polly.ResilienceContext! context, TState state) -> System.Threading.Tasks.ValueTask> abstract Polly.Registry.ResilienceStrategyProvider.TryGetStrategy(TKey key, out Polly.ResilienceStrategy? strategy) -> bool abstract Polly.Registry.ResilienceStrategyProvider.TryGetStrategy(TKey key, out Polly.ResilienceStrategy? strategy) -> bool abstract Polly.ResilienceContextPool.Get(string? operationKey, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Polly.ResilienceContext! abstract Polly.ResilienceContextPool.Return(Polly.ResilienceContext! context) -> void -abstract Polly.ResilienceStrategy.ExecuteCore(System.Func>>! callback, Polly.ResilienceContext! context, TState state) -> System.Threading.Tasks.ValueTask> override Polly.Outcome.ToString() -> string! override Polly.Registry.ResilienceStrategyRegistry.TryGetStrategy(TKey key, out Polly.ResilienceStrategy? strategy) -> bool override Polly.Registry.ResilienceStrategyRegistry.TryGetStrategy(TKey key, out Polly.ResilienceStrategy? strategy) -> bool @@ -147,6 +147,8 @@ Polly.Hedging.OnHedgingArguments.HasOutcome.get -> bool Polly.Hedging.OnHedgingArguments.OnHedgingArguments(int attemptNumber, bool hasOutcome, System.TimeSpan executionTime) -> void Polly.HedgingCompositeStrategyBuilderExtensions Polly.LegacySupport +Polly.NonReactiveResilienceStrategy +Polly.NonReactiveResilienceStrategy.NonReactiveResilienceStrategy() -> void Polly.NullResilienceStrategy Polly.NullResilienceStrategy Polly.Outcome @@ -262,7 +264,6 @@ Polly.ResilienceStrategy.ExecuteAsync(System.Func(System.Func! callback, Polly.ResilienceContext! context, TState state) -> System.Threading.Tasks.ValueTask Polly.ResilienceStrategy.ExecuteAsync(System.Func! callback, TState state, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask Polly.ResilienceStrategy.ExecuteOutcomeAsync(System.Func>>! callback, Polly.ResilienceContext! context, TState state) -> System.Threading.Tasks.ValueTask> -Polly.ResilienceStrategy.ResilienceStrategy() -> void Polly.ResilienceStrategy Polly.ResilienceStrategy.Execute(System.Func! callback, Polly.ResilienceContext! context, TState state) -> TResult Polly.ResilienceStrategy.Execute(System.Func! callback, TState state, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> TResult @@ -389,7 +390,7 @@ static Polly.CircuitBreakerCompositeStrategyBuilderExtensions.AddCircuitBreaker( static Polly.CircuitBreakerCompositeStrategyBuilderExtensions.AddCircuitBreaker(this Polly.CompositeStrategyBuilder! builder, Polly.CircuitBreaker.CircuitBreakerStrategyOptions! options) -> Polly.CompositeStrategyBuilder! static Polly.CompositeStrategyBuilderExtensions.AddStrategy(this Polly.CompositeStrategyBuilder! builder, System.Func!>! factory, Polly.ResilienceStrategyOptions! options) -> Polly.CompositeStrategyBuilder! static Polly.CompositeStrategyBuilderExtensions.AddStrategy(this TBuilder! builder, Polly.ResilienceStrategy! strategy) -> TBuilder! -static Polly.CompositeStrategyBuilderExtensions.AddStrategy(this TBuilder! builder, System.Func! factory, Polly.ResilienceStrategyOptions! options) -> TBuilder! +static Polly.CompositeStrategyBuilderExtensions.AddStrategy(this TBuilder! builder, System.Func! factory, Polly.ResilienceStrategyOptions! options) -> TBuilder! static Polly.CompositeStrategyBuilderExtensions.AddStrategy(this Polly.CompositeStrategyBuilder! builder, Polly.ResilienceStrategy! strategy) -> Polly.CompositeStrategyBuilder! static Polly.CompositeStrategyBuilderExtensions.AddStrategy(this Polly.CompositeStrategyBuilder! builder, System.Func!>! factory, Polly.ResilienceStrategyOptions! options) -> Polly.CompositeStrategyBuilder! static Polly.FallbackCompositeStrategyBuilderExtensions.AddFallback(this Polly.CompositeStrategyBuilder! builder, Polly.Fallback.FallbackStrategyOptions! options) -> Polly.CompositeStrategyBuilder! diff --git a/src/Polly.Core/ReactiveResilienceStrategy.cs b/src/Polly.Core/ReactiveResilienceStrategy.cs index 7eeec7911f0..5a8a4e1886e 100644 --- a/src/Polly.Core/ReactiveResilienceStrategy.cs +++ b/src/Polly.Core/ReactiveResilienceStrategy.cs @@ -11,7 +11,7 @@ public abstract class ReactiveResilienceStrategy { /// - /// An implementation of resilience strategy that executes the specified . + /// An implementation of a reactive resilience strategy that executes the specified . /// /// The type of state associated with the callback. /// The user-provided callback. diff --git a/src/Polly.Core/ResilienceStrategy.cs b/src/Polly.Core/ResilienceStrategy.cs index 2ce226716a6..5989b5b865d 100644 --- a/src/Polly.Core/ResilienceStrategy.cs +++ b/src/Polly.Core/ResilienceStrategy.cs @@ -1,7 +1,5 @@ namespace Polly; -#pragma warning disable CA1031 // Do not catch general exception types - /// /// Resilience strategy is used to execute the user-provided callbacks. /// @@ -9,37 +7,17 @@ namespace Polly; /// Resilience strategy supports various types of callbacks and provides a unified way to execute them. /// This includes overloads for synchronous and asynchronous callbacks, generic and non-generic callbacks. /// -public abstract partial class ResilienceStrategy +public partial class ResilienceStrategy { internal static ResilienceContextPool Pool => ResilienceContextPool.Shared; internal ResilienceStrategyOptions? Options { get; set; } - /// - /// An implementation of resilience strategy that executes the specified . - /// - /// The type of result returned by the callback. - /// The type of state associated with the callback. - /// The user-provided callback. - /// The context associated with the callback. - /// The state associated with the callback. - /// - /// An instance of a pending for asynchronous executions or a completed task for synchronous executions. - /// - /// - /// This method is called for both synchronous and asynchronous execution flows. - /// - /// You can use to determine whether is synchronous or asynchronous. - /// This is useful when the custom strategy behaves differently in each execution flow. In general, for most strategies, the implementation - /// is the same for both execution flows. - /// See for more details. - /// - /// - /// The provided callback never throws an exception. Instead, the exception is captured and converted to an . - /// Similarly, do not throw exceptions from your strategy implementation. Instead, return an exception instance as . - /// - /// - protected internal abstract ValueTask> ExecuteCore( + internal ResilienceStrategy() + { + } + + internal abstract ValueTask> ExecuteCore( Func>> callback, ResilienceContext context, TState state); diff --git a/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs b/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs index db8975d8bed..d0252768a62 100644 --- a/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs +++ b/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs @@ -2,7 +2,7 @@ namespace Polly.Timeout; -internal sealed class TimeoutResilienceStrategy : ResilienceStrategy +internal sealed class TimeoutResilienceStrategy : NonReactiveResilienceStrategy { private readonly ResilienceStrategyTelemetry _telemetry; private readonly CancellationTokenSourcePool _cancellationTokenSourcePool; diff --git a/src/Polly.Core/Utils/CompositeResilienceStrategy.cs b/src/Polly.Core/Utils/CompositeResilienceStrategy.cs index b6fe104994b..d620791ea8e 100644 --- a/src/Polly.Core/Utils/CompositeResilienceStrategy.cs +++ b/src/Polly.Core/Utils/CompositeResilienceStrategy.cs @@ -55,7 +55,7 @@ private CompositeResilienceStrategy(ResilienceStrategy first, IReadOnlyList Strategies { get; } - protected internal override ValueTask> ExecuteCore( + internal override ValueTask> ExecuteCore( Func>> callback, ResilienceContext context, TState state) { @@ -78,7 +78,7 @@ private sealed class DelegatingResilienceStrategy : ResilienceStrategy public ResilienceStrategy? Next { get; set; } - protected internal override ValueTask> ExecuteCore( + internal override ValueTask> ExecuteCore( Func>> callback, ResilienceContext context, TState state) diff --git a/src/Polly.Core/Utils/NonReactiveResilienceStrategyBridge.cs b/src/Polly.Core/Utils/NonReactiveResilienceStrategyBridge.cs new file mode 100644 index 00000000000..82db403244d --- /dev/null +++ b/src/Polly.Core/Utils/NonReactiveResilienceStrategyBridge.cs @@ -0,0 +1,14 @@ +namespace Polly.Utils; + +[DebuggerDisplay("{Strategy}")] +internal sealed class NonReactiveResilienceStrategyBridge : ResilienceStrategy +{ + public NonReactiveResilienceStrategyBridge(NonReactiveResilienceStrategy strategy) => Strategy = strategy; + + public NonReactiveResilienceStrategy Strategy { get; } + + internal override ValueTask> ExecuteCore( + Func>> callback, + ResilienceContext context, + TState state) => Strategy.ExecuteCore(callback, context, state); +} diff --git a/src/Polly.Core/Utils/ReactiveResilienceStrategyBridge.cs b/src/Polly.Core/Utils/ReactiveResilienceStrategyBridge.cs index de38ad664df..c76d82fab4a 100644 --- a/src/Polly.Core/Utils/ReactiveResilienceStrategyBridge.cs +++ b/src/Polly.Core/Utils/ReactiveResilienceStrategyBridge.cs @@ -7,7 +7,7 @@ internal sealed class ReactiveResilienceStrategyBridge : ResilienceStrategy public ReactiveResilienceStrategy Strategy { get; } - protected internal override ValueTask> ExecuteCore( + internal override ValueTask> ExecuteCore( Func>> callback, ResilienceContext context, TState state) diff --git a/src/Polly.Core/Utils/ReloadableResilienceStrategy.cs b/src/Polly.Core/Utils/ReloadableResilienceStrategy.cs index dc33a17766a..90dcd420582 100644 --- a/src/Polly.Core/Utils/ReloadableResilienceStrategy.cs +++ b/src/Polly.Core/Utils/ReloadableResilienceStrategy.cs @@ -30,7 +30,7 @@ public ReloadableResilienceStrategy( public ResilienceStrategy Strategy { get; private set; } - protected internal override ValueTask> ExecuteCore( + internal override ValueTask> ExecuteCore( Func>> callback, ResilienceContext context, TState state) diff --git a/src/Polly.Extensions/Telemetry/TelemetryCompositeStrategyBuilderExtensions.cs b/src/Polly.Extensions/Telemetry/TelemetryCompositeStrategyBuilderExtensions.cs index 10cf5f21422..a501033f189 100644 --- a/src/Polly.Extensions/Telemetry/TelemetryCompositeStrategyBuilderExtensions.cs +++ b/src/Polly.Extensions/Telemetry/TelemetryCompositeStrategyBuilderExtensions.cs @@ -44,6 +44,7 @@ public static TBuilder ConfigureTelemetry(this TBuilder builder, ILogg /// /// Thrown when or is . [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(TelemetryOptions))] + [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(TelemetryStrategyOptions))] public static TBuilder ConfigureTelemetry(this TBuilder builder, TelemetryOptions options) where TBuilder : CompositeStrategyBuilderBase { @@ -62,9 +63,15 @@ public static TBuilder ConfigureTelemetry(this TBuilder builder, Telem options.ResultFormatter, options.Enrichers.ToList()); - strategies.Insert(0, telemetryStrategy); +#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code + strategies.Insert(0, new CompositeStrategyBuilder().AddStrategy(_ => telemetryStrategy, new TelemetryStrategyOptions()).Build()); +#pragma warning restore IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code }; return builder; } + + private sealed class TelemetryStrategyOptions : ResilienceStrategyOptions + { + } } diff --git a/src/Polly.Extensions/Telemetry/TelemetryResilienceStrategy.cs b/src/Polly.Extensions/Telemetry/TelemetryResilienceStrategy.cs index c52f6867ba1..2c090a4614e 100644 --- a/src/Polly.Extensions/Telemetry/TelemetryResilienceStrategy.cs +++ b/src/Polly.Extensions/Telemetry/TelemetryResilienceStrategy.cs @@ -3,7 +3,7 @@ namespace Polly.Telemetry; -internal sealed class TelemetryResilienceStrategy : ResilienceStrategy +internal sealed class TelemetryResilienceStrategy : NonReactiveResilienceStrategy { private readonly TimeProvider _timeProvider; private readonly string? _builderName; diff --git a/src/Polly.RateLimiting/RateLimiterResilienceStrategy.cs b/src/Polly.RateLimiting/RateLimiterResilienceStrategy.cs index 0508d2c9160..e87120f188e 100644 --- a/src/Polly.RateLimiting/RateLimiterResilienceStrategy.cs +++ b/src/Polly.RateLimiting/RateLimiterResilienceStrategy.cs @@ -3,7 +3,7 @@ namespace Polly.RateLimiting; -internal sealed class RateLimiterResilienceStrategy : ResilienceStrategy +internal sealed class RateLimiterResilienceStrategy : NonReactiveResilienceStrategy { private readonly ResilienceStrategyTelemetry _telemetry; diff --git a/src/Polly.Testing/ResilienceStrategyExtensions.cs b/src/Polly.Testing/ResilienceStrategyExtensions.cs index e94b161bfa4..5a115f5c66f 100644 --- a/src/Polly.Testing/ResilienceStrategyExtensions.cs +++ b/src/Polly.Testing/ResilienceStrategyExtensions.cs @@ -51,9 +51,14 @@ private static InnerStrategiesDescriptor GetInnerStrategiesCore(ResilienceStr private static Type GetStrategyType(ResilienceStrategy strategy) { - if (strategy is ReactiveResilienceStrategyBridge bridge) + if (strategy is ReactiveResilienceStrategyBridge reactiveBridge) { - return bridge.Strategy.GetType(); + return reactiveBridge.Strategy.GetType(); + } + + if (strategy is NonReactiveResilienceStrategyBridge nonReactiveBridge) + { + return nonReactiveBridge.Strategy.GetType(); } return strategy.GetType(); diff --git a/test/Polly.Core.Tests/CompositeStrategyBuilderTests.cs b/test/Polly.Core.Tests/CompositeStrategyBuilderTests.cs index 2ef3d786dc6..8856e8ed167 100644 --- a/test/Polly.Core.Tests/CompositeStrategyBuilderTests.cs +++ b/test/Polly.Core.Tests/CompositeStrategyBuilderTests.cs @@ -54,14 +54,14 @@ public void AddStrategy_Single_Ok() After = (_, _) => executions.Add(3), }; - builder.AddStrategy(first); + builder.AddStrategy(first.AsStrategy()); // act var strategy = builder.Build(); // assert strategy.Execute(_ => executions.Add(2)); - strategy.Should().BeOfType(); + ((NonReactiveResilienceStrategyBridge)strategy).Strategy.Should().BeOfType(); executions.Should().BeInAscendingOrder(); executions.Should().HaveCount(3); } @@ -88,9 +88,9 @@ public void AddStrategy_Multiple_Ok() After = (_, _) => executions.Add(5), }; - builder.AddStrategy(first); - builder.AddStrategy(second); - builder.AddStrategy(third); + builder.AddStrategy(first.AsStrategy()); + builder.AddStrategy(second.AsStrategy()); + builder.AddStrategy(third.AsStrategy()); // act var strategy = builder.Build(); @@ -164,9 +164,9 @@ public void AddStrategy_MultipleNonDelegating_Ok() After = () => executions.Add(5), }; - builder.AddStrategy(first); - builder.AddStrategy(second); - builder.AddStrategy(third); + builder.AddStrategy(first.AsStrategy()); + builder.AddStrategy(second.AsStrategy()); + builder.AddStrategy(third.AsStrategy()); // act var strategy = builder.Build(); @@ -218,7 +218,7 @@ public void AddStrategy_InvalidOptions_Throws() var builder = new CompositeStrategyBuilder(); builder - .Invoking(b => b.AddStrategy(_ => NullResilienceStrategy.Instance, new InvalidResilienceStrategyOptions())) + .Invoking(b => b.AddStrategy(_ => new TestResilienceStrategy(), new InvalidResilienceStrategyOptions())) .Should() .Throw() .WithMessage( @@ -236,7 +236,7 @@ public void AddStrategy_NullFactory_Throws() var builder = new CompositeStrategyBuilder(); builder - .Invoking(b => b.AddStrategy((Func)null!, new TestResilienceStrategyOptions())) + .Invoking(b => b.AddStrategy((Func)null!, new TestResilienceStrategyOptions())) .Should() .Throw() .And.ParamName @@ -268,14 +268,14 @@ public void AddStrategy_CombinePipelines_Ok() After = (_, _) => executions.Add(6), }; - var pipeline1 = new CompositeStrategyBuilder().AddStrategy(first).AddStrategy(second).Build(); + var pipeline1 = new CompositeStrategyBuilder().AddStrategy(first.AsStrategy()).AddStrategy(second.AsStrategy()).Build(); var third = new TestResilienceStrategy { Before = (_, _) => executions.Add(3), After = (_, _) => executions.Add(5), }; - var pipeline2 = new CompositeStrategyBuilder().AddStrategy(third).Build(); + var pipeline2 = new CompositeStrategyBuilder().AddStrategy(third.AsStrategy()).Build(); // act var strategy = new CompositeStrategyBuilder().AddStrategy(pipeline1).AddStrategy(pipeline2).Build(); @@ -341,13 +341,13 @@ public void BuildStrategy_EnsureCorrectContext() public void Build_OnCreatingStrategy_EnsureRespected() { // arrange - var strategy = new TestResilienceStrategy(); + var strategy = new TestResilienceStrategy().AsStrategy(); var builder = new CompositeStrategyBuilder { OnCreatingStrategy = strategies => { strategies.Should().ContainSingle(s => s == strategy); - strategies.Insert(0, new TestResilienceStrategy()); + strategies.Insert(0, new TestResilienceStrategy().AsStrategy()); } }; @@ -372,19 +372,19 @@ public void ExecuteAsync_EnsureReceivedCallbackExecutesNextStrategy() { Before = (_, _) => executions.Add("first-start"), After = (_, _) => executions.Add("first-end"), - }; + }.AsStrategy(); var second = new ExecuteCallbackTwiceStrategy { Before = () => executions.Add("second-start"), After = () => executions.Add("second-end"), - }; + }.AsStrategy(); var third = new TestResilienceStrategy { Before = (_, _) => executions.Add("third-start"), After = (_, _) => executions.Add("third-end"), - }; + }.AsStrategy(); var strategy = new CompositeStrategyBuilder().AddStrategy(first).AddStrategy(second).AddStrategy(third).Build(); @@ -409,7 +409,7 @@ public void ExecuteAsync_EnsureReceivedCallbackExecutesNextStrategy() .BeTrue(); } - private class Strategy : ResilienceStrategy + private class Strategy : NonReactiveResilienceStrategy { public Action? Before { get; set; } @@ -432,7 +432,7 @@ protected internal override async ValueTask> ExecuteCore(new TestResilienceStrategy()); + var testStrategy = new ResilienceStrategy(new TestResilienceStrategy().AsStrategy()); _builder.AddStrategy(testStrategy); // act diff --git a/test/Polly.Core.Tests/Registry/ResilienceStrategyProviderTests.cs b/test/Polly.Core.Tests/Registry/ResilienceStrategyProviderTests.cs index 74525e49288..d5abfe3aac2 100644 --- a/test/Polly.Core.Tests/Registry/ResilienceStrategyProviderTests.cs +++ b/test/Polly.Core.Tests/Registry/ResilienceStrategyProviderTests.cs @@ -29,7 +29,7 @@ public void Get_GenericDoesNotExist_Throws() [Fact] public void Get_Exist_Ok() { - var provider = new Provider { Strategy = new TestResilienceStrategy() }; + var provider = new Provider { Strategy = new TestResilienceStrategy().AsStrategy() }; provider.GetStrategy("exists").Should().Be(provider.Strategy); } diff --git a/test/Polly.Core.Tests/Registry/ResilienceStrategyRegistryTests.cs b/test/Polly.Core.Tests/Registry/ResilienceStrategyRegistryTests.cs index 69fc6477b4c..ae18cef3d75 100644 --- a/test/Polly.Core.Tests/Registry/ResilienceStrategyRegistryTests.cs +++ b/test/Polly.Core.Tests/Registry/ResilienceStrategyRegistryTests.cs @@ -2,6 +2,7 @@ using Polly.Registry; using Polly.Retry; using Polly.Timeout; +using Polly.Utils; namespace Polly.Core.Tests.Registry; @@ -44,9 +45,9 @@ public void Clear_Ok() registry.TryAddBuilder("C", (b, _) => b.AddStrategy(new TestResilienceStrategy())); - registry.TryAddStrategy("A", new TestResilienceStrategy()); - registry.TryAddStrategy("B", new TestResilienceStrategy()); - registry.TryAddStrategy("C", new TestResilienceStrategy()); + registry.TryAddStrategy("A", new TestResilienceStrategy().AsStrategy()); + registry.TryAddStrategy("B", new TestResilienceStrategy().AsStrategy()); + registry.TryAddStrategy("C", new TestResilienceStrategy().AsStrategy()); registry.ClearStrategies(); @@ -78,8 +79,8 @@ public void Remove_Ok() { var registry = new ResilienceStrategyRegistry(); - registry.TryAddStrategy("A", new TestResilienceStrategy()); - registry.TryAddStrategy("B", new TestResilienceStrategy()); + registry.TryAddStrategy("A", new TestResilienceStrategy().AsStrategy()); + registry.TryAddStrategy("B", new TestResilienceStrategy().AsStrategy()); registry.RemoveStrategy("A").Should().BeTrue(); registry.RemoveStrategy("A").Should().BeFalse(); @@ -302,7 +303,7 @@ public void TryGet_GenericNoBuilder_Null() [Fact] public void TryGet_ExplicitStrategyAdded_Ok() { - var expectedStrategy = new TestResilienceStrategy(); + var expectedStrategy = new TestResilienceStrategy().AsStrategy(); var registry = CreateRegistry(); var key = StrategyId.Create("A", "Instance"); registry.TryAddStrategy(key, expectedStrategy).Should().BeTrue(); @@ -328,12 +329,12 @@ public void TryGet_GenericExplicitStrategyAdded_Ok() [Fact] public void TryAdd_Twice_SecondNotAdded() { - var expectedStrategy = new TestResilienceStrategy(); + var expectedStrategy = new TestResilienceStrategy().AsStrategy(); var registry = CreateRegistry(); var key = StrategyId.Create("A", "Instance"); registry.TryAddStrategy(key, expectedStrategy).Should().BeTrue(); - registry.TryAddStrategy(key, new TestResilienceStrategy()).Should().BeFalse(); + registry.TryAddStrategy(key, new TestResilienceStrategy().AsStrategy()).Should().BeFalse(); registry.TryGetStrategy(key, out var strategy).Should().BeTrue(); strategy.Should().BeSameAs(expectedStrategy); @@ -435,7 +436,7 @@ public void GetOrAddStrategy_Ok() var strategy = registry.GetOrAddStrategy(id, builder => { builder.AddTimeout(TimeSpan.FromSeconds(1)); called++; }); var otherStrategy = registry.GetOrAddStrategy(id, builder => { builder.AddTimeout(TimeSpan.FromSeconds(1)); called++; }); - strategy.Should().BeOfType(); + ((NonReactiveResilienceStrategyBridge)strategy).Strategy.Should().BeOfType(); strategy.Should().BeSameAs(otherStrategy); called.Should().Be(1); } @@ -450,7 +451,7 @@ public void GetOrAddStrategy_Generic_Ok() var strategy = registry.GetOrAddStrategy(id, builder => { builder.AddTimeout(TimeSpan.FromSeconds(1)); called++; }); var otherStrategy = registry.GetOrAddStrategy(id, builder => { builder.AddTimeout(TimeSpan.FromSeconds(1)); called++; }); - strategy.Strategy.Should().BeOfType(); + ((NonReactiveResilienceStrategyBridge)strategy.Strategy).Strategy.Should().BeOfType(); strategy.Should().BeSameAs(otherStrategy); } diff --git a/test/Polly.Core.Tests/ResilienceStrategyTTests.Async.cs b/test/Polly.Core.Tests/ResilienceStrategyTTests.Async.cs index c586252c469..e1a3f88d278 100644 --- a/test/Polly.Core.Tests/ResilienceStrategyTTests.Async.cs +++ b/test/Polly.Core.Tests/ResilienceStrategyTTests.Async.cs @@ -71,7 +71,7 @@ public async Task ExecuteAsync_GenericStrategy_Ok(Func> execut c.IsSynchronous.Should().BeTrue(); c.ResultType.Should().Be(typeof(string)); }, - }); + }.AsStrategy()); execute(strategy); } diff --git a/test/Polly.Core.Tests/ResilienceStrategyTests.Async.cs b/test/Polly.Core.Tests/ResilienceStrategyTests.Async.cs index 7f360bbaec3..190fb5b2a87 100644 --- a/test/Polly.Core.Tests/ResilienceStrategyTests.Async.cs +++ b/test/Polly.Core.Tests/ResilienceStrategyTests.Async.cs @@ -78,7 +78,7 @@ public async Task ExecuteAsync_Ok(ExecuteParameters parameters) context = c; parameters.AssertContext(c); }, - }; + }.AsStrategy(); var result = await parameters.Execute(strategy); @@ -96,7 +96,7 @@ public async Task ExecuteAsync_EnsureCallStackPreserved() static async ValueTask AssertStackTrace(Func execute) { - var strategy = new TestResilienceStrategy(); + var strategy = new TestResilienceStrategy().AsStrategy(); var error = await strategy .Invoking(s => diff --git a/test/Polly.Core.Tests/ResilienceStrategyTests.AsyncT.cs b/test/Polly.Core.Tests/ResilienceStrategyTests.AsyncT.cs index edf8a4724eb..cca7b3c2b7c 100644 --- a/test/Polly.Core.Tests/ResilienceStrategyTests.AsyncT.cs +++ b/test/Polly.Core.Tests/ResilienceStrategyTests.AsyncT.cs @@ -82,7 +82,7 @@ public async Task ExecuteAsyncT_Ok(ExecuteParameters parameters) }, }; - var result = await parameters.Execute(strategy); + var result = await parameters.Execute(strategy.AsStrategy()); parameters.AssertContextAfter(context!); parameters.AssertResult(result); @@ -98,7 +98,7 @@ public async Task ExecuteAsync_T_EnsureCallStackPreserved() static async ValueTask AssertStackTrace(Func> execute) { - var strategy = new TestResilienceStrategy(); + var strategy = new TestResilienceStrategy().AsStrategy(); var error = await strategy .Invoking(s => diff --git a/test/Polly.Core.Tests/ResilienceStrategyTests.Sync.cs b/test/Polly.Core.Tests/ResilienceStrategyTests.Sync.cs index 461638d4238..004d4638bc3 100644 --- a/test/Polly.Core.Tests/ResilienceStrategyTests.Sync.cs +++ b/test/Polly.Core.Tests/ResilienceStrategyTests.Sync.cs @@ -82,7 +82,7 @@ public async Task Execute_Ok(ExecuteParameters parameters) context = c; parameters.AssertContext(c); }, - }; + }.AsStrategy(); var result = await parameters.Execute(strategy); @@ -103,7 +103,7 @@ public void Execute_EnsureCallStackPreserved() static void AssertStackTrace(Action execute) { - var strategy = new TestResilienceStrategy(); + var strategy = new TestResilienceStrategy().AsStrategy(); var error = strategy .Invoking(s => execute(s)) diff --git a/test/Polly.Core.Tests/ResilienceStrategyTests.SyncT.cs b/test/Polly.Core.Tests/ResilienceStrategyTests.SyncT.cs index 4cd89f7294e..670cc7600e7 100644 --- a/test/Polly.Core.Tests/ResilienceStrategyTests.SyncT.cs +++ b/test/Polly.Core.Tests/ResilienceStrategyTests.SyncT.cs @@ -84,7 +84,7 @@ public async Task ExecuteT_Ok(ExecuteParameters parameters) context = c; parameters.AssertContext(c); }, - }; + }.AsStrategy(); var result = await parameters.Execute(strategy); @@ -105,7 +105,7 @@ public void Execute_T_EnsureCallStackPreserved() static void AssertStackTrace(Func execute) { - var strategy = new TestResilienceStrategy(); + var strategy = new TestResilienceStrategy().AsStrategy(); var error = strategy .Invoking(s => execute(s)) diff --git a/test/Polly.Core.Tests/ResilienceStrategyTests.cs b/test/Polly.Core.Tests/ResilienceStrategyTests.cs index a0036bc17a7..b0e867942d4 100644 --- a/test/Polly.Core.Tests/ResilienceStrategyTests.cs +++ b/test/Polly.Core.Tests/ResilienceStrategyTests.cs @@ -11,8 +11,8 @@ public void DebuggerProxy_Ok() { var pipeline = CompositeResilienceStrategy.Create(new[] { - new TestResilienceStrategy(), - new TestResilienceStrategy() + new TestResilienceStrategy().AsStrategy(), + new TestResilienceStrategy().AsStrategy() }); new CompositeResilienceStrategy.DebuggerProxy(pipeline).Strategies.Should().HaveCount(2); diff --git a/test/Polly.Core.Tests/Timeout/TimeoutCompositeStrategyBuilderExtensionsTests.cs b/test/Polly.Core.Tests/Timeout/TimeoutCompositeStrategyBuilderExtensionsTests.cs index c913b9b2129..1e61f5bc7ad 100644 --- a/test/Polly.Core.Tests/Timeout/TimeoutCompositeStrategyBuilderExtensionsTests.cs +++ b/test/Polly.Core.Tests/Timeout/TimeoutCompositeStrategyBuilderExtensionsTests.cs @@ -1,5 +1,6 @@ using System.ComponentModel.DataAnnotations; using Polly.Timeout; +using Polly.Utils; namespace Polly.Core.Tests.Timeout; @@ -31,7 +32,8 @@ internal void AddTimeout_Ok(TimeSpan timeout, Action(); configure(builder); - var strategy = builder.Build().Strategy.Should().BeOfType().Subject; + + var strategy = ((NonReactiveResilienceStrategyBridge)builder.Build().Strategy).Strategy.Should().BeOfType().Subject; assert(strategy); GetTimeout(strategy).Should().Be(timeout); @@ -42,7 +44,7 @@ public void AddTimeout_Options_Ok() { var strategy = new CompositeStrategyBuilder().AddTimeout(new TimeoutStrategyOptions()).Build(); - strategy.Should().BeOfType(); + ((NonReactiveResilienceStrategyBridge)strategy).Strategy.Should().BeOfType(); } [Fact] diff --git a/test/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyTests.cs b/test/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyTests.cs index 978bdfb199a..20f07e0684a 100644 --- a/test/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyTests.cs +++ b/test/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyTests.cs @@ -234,5 +234,5 @@ await sut.ExecuteAsync(async token => private void SetTimeout(TimeSpan timeout) => _options.TimeoutGenerator = args => new ValueTask(timeout); - private TimeoutResilienceStrategy CreateSut() => new(_options, _timeProvider, _telemetry); + private ResilienceStrategy CreateSut() => new TimeoutResilienceStrategy(_options, _timeProvider, _telemetry).AsStrategy(); } diff --git a/test/Polly.Core.Tests/Utils/CompositeResilienceStrategyTests.cs b/test/Polly.Core.Tests/Utils/CompositeResilienceStrategyTests.cs index ac1c16d80a3..d35ebb67bef 100644 --- a/test/Polly.Core.Tests/Utils/CompositeResilienceStrategyTests.cs +++ b/test/Polly.Core.Tests/Utils/CompositeResilienceStrategyTests.cs @@ -9,7 +9,7 @@ public void Create_ArgValidation() { Assert.Throws(() => CompositeResilienceStrategy.Create(null!)); Assert.Throws(() => CompositeResilienceStrategy.Create(Array.Empty())); - Assert.Throws(() => CompositeResilienceStrategy.Create(new ResilienceStrategy[] { new TestResilienceStrategy() })); + Assert.Throws(() => CompositeResilienceStrategy.Create(new[] { new TestResilienceStrategy().AsStrategy() })); Assert.Throws(() => CompositeResilienceStrategy.Create(new ResilienceStrategy[] { NullResilienceStrategy.Instance, @@ -20,11 +20,11 @@ public void Create_ArgValidation() [Fact] public void Create_EnsureOriginalStrategiesPreserved() { - var strategies = new ResilienceStrategy[] + var strategies = new[] { - new TestResilienceStrategy(), - new Strategy(), - new TestResilienceStrategy(), + new TestResilienceStrategy().AsStrategy(), + new Strategy().AsStrategy(), + new TestResilienceStrategy().AsStrategy(), }; var pipeline = CompositeResilienceStrategy.Create(strategies); @@ -40,10 +40,10 @@ public void Create_EnsureOriginalStrategiesPreserved() [Fact] public async Task Create_EnsureExceptionsNotWrapped() { - var strategies = new ResilienceStrategy[] + var strategies = new[] { - new Strategy(), - new Strategy(), + new Strategy().AsStrategy(), + new Strategy().AsStrategy(), }; var pipeline = CompositeResilienceStrategy.Create(strategies); @@ -56,11 +56,11 @@ await pipeline [Fact] public void Create_EnsurePipelineReusableAcrossDifferentPipelines() { - var strategies = new ResilienceStrategy[] + var strategies = new[] { - new TestResilienceStrategy(), - new Strategy(), - new TestResilienceStrategy(), + new TestResilienceStrategy().AsStrategy(), + new Strategy().AsStrategy(), + new TestResilienceStrategy().AsStrategy(), }; var pipeline = CompositeResilienceStrategy.Create(strategies); @@ -77,10 +77,10 @@ public async Task Create_Cancelled_EnsureNoExecution() { using var cancellation = new CancellationTokenSource(); cancellation.Cancel(); - var strategies = new ResilienceStrategy[] + var strategies = new[] { - new TestResilienceStrategy(), - new TestResilienceStrategy(), + new TestResilienceStrategy().AsStrategy(), + new TestResilienceStrategy().AsStrategy(), }; var pipeline = CompositeResilienceStrategy.Create(strategies); @@ -96,10 +96,10 @@ public async Task Create_CancelledLater_EnsureNoExecution() { var executed = false; using var cancellation = new CancellationTokenSource(); - var strategies = new ResilienceStrategy[] + var strategies = new[] { - new TestResilienceStrategy { Before = (_, _) => { executed = true; cancellation.Cancel(); } }, - new TestResilienceStrategy(), + new TestResilienceStrategy { Before = (_, _) => { executed = true; cancellation.Cancel(); } }.AsStrategy(), + new TestResilienceStrategy().AsStrategy(), }; var pipeline = CompositeResilienceStrategy.Create(strategies); @@ -111,7 +111,7 @@ public async Task Create_CancelledLater_EnsureNoExecution() executed.Should().BeTrue(); } - private class Strategy : ResilienceStrategy + private class Strategy : NonReactiveResilienceStrategy { protected internal override async ValueTask> ExecuteCore( Func>> callback, diff --git a/test/Polly.Core.Tests/Utils/ReloadableResilienceStrategyTests.cs b/test/Polly.Core.Tests/Utils/ReloadableResilienceStrategyTests.cs index a5c22f99145..ff735890cd3 100644 --- a/test/Polly.Core.Tests/Utils/ReloadableResilienceStrategyTests.cs +++ b/test/Polly.Core.Tests/Utils/ReloadableResilienceStrategyTests.cs @@ -24,7 +24,7 @@ public ReloadableResilienceStrategyTests() [Fact] public void Ctor_Ok() { - var strategy = new TestResilienceStrategy(); + var strategy = new TestResilienceStrategy().AsStrategy(); var sut = CreateSut(strategy); sut.Strategy.Should().Be(strategy); @@ -35,7 +35,7 @@ public void Ctor_Ok() [Fact] public void ChangeTriggered_StrategyReloaded() { - var strategy = new TestResilienceStrategy(); + var strategy = new TestResilienceStrategy().AsStrategy(); var sut = CreateSut(strategy); for (var i = 0; i < 10; i++) @@ -54,7 +54,7 @@ public void ChangeTriggered_StrategyReloaded() [Fact] public void ChangeTriggered_FactoryError_LastStrategyUsedAndErrorReported() { - var strategy = new TestResilienceStrategy(); + var strategy = new TestResilienceStrategy().AsStrategy(); var sut = CreateSut(strategy, () => throw new InvalidOperationException()); _cancellationTokenSource.Cancel(); @@ -78,10 +78,10 @@ public void ChangeTriggered_FactoryError_LastStrategyUsedAndErrorReported() private ReloadableResilienceStrategy CreateSut(ResilienceStrategy? initial = null, Func? factory = null) { - factory ??= () => new TestResilienceStrategy(); + factory ??= () => new TestResilienceStrategy().AsStrategy(); return new( - initial ?? new TestResilienceStrategy(), + initial ?? new TestResilienceStrategy().AsStrategy(), () => _cancellationTokenSource.Token, factory, _telemetry); diff --git a/test/Polly.Extensions.Tests/DependencyInjection/PollyServiceCollectionExtensionTests.cs b/test/Polly.Extensions.Tests/DependencyInjection/PollyServiceCollectionExtensionTests.cs index 07a08d349f4..88d082229e5 100644 --- a/test/Polly.Extensions.Tests/DependencyInjection/PollyServiceCollectionExtensionTests.cs +++ b/test/Polly.Extensions.Tests/DependencyInjection/PollyServiceCollectionExtensionTests.cs @@ -304,7 +304,7 @@ private ResilienceStrategyProvider CreateProvider() return _services.BuildServiceProvider().GetRequiredService>(); } - private class TestStrategy : ResilienceStrategy + private class TestStrategy : NonReactiveResilienceStrategy { protected override ValueTask> ExecuteCore( Func>> callback, diff --git a/test/Polly.Extensions.Tests/Issues/IssuesTests.OnCircuitBreakWithServiceProvider_796.cs b/test/Polly.Extensions.Tests/Issues/IssuesTests.OnCircuitBreakWithServiceProvider_796.cs index 54579ada880..292002536dd 100644 --- a/test/Polly.Extensions.Tests/Issues/IssuesTests.OnCircuitBreakWithServiceProvider_796.cs +++ b/test/Polly.Extensions.Tests/Issues/IssuesTests.OnCircuitBreakWithServiceProvider_796.cs @@ -54,7 +54,7 @@ public async Task OnCircuitBreakWithServiceProvider_796() contextChecked.Should().BeTrue(); } - private class ServiceProviderStrategy : ResilienceStrategy + private class ServiceProviderStrategy : NonReactiveResilienceStrategy { private readonly IServiceProvider _serviceProvider; diff --git a/test/Polly.Extensions.Tests/ReloadableResilienceStrategyTests.cs b/test/Polly.Extensions.Tests/ReloadableResilienceStrategyTests.cs index 25c8382ef2f..ad7a7179648 100644 --- a/test/Polly.Extensions.Tests/ReloadableResilienceStrategyTests.cs +++ b/test/Polly.Extensions.Tests/ReloadableResilienceStrategyTests.cs @@ -55,7 +55,7 @@ public void AddResilienceStrategy_EnsureReloadable(string? name) } } - public class ReloadableStrategy : ResilienceStrategy + public class ReloadableStrategy : NonReactiveResilienceStrategy { public ReloadableStrategy(string tag) => Tag = tag; diff --git a/test/Polly.Extensions.Tests/Telemetry/TelemetryResilienceStrategyTests.cs b/test/Polly.Extensions.Tests/Telemetry/TelemetryResilienceStrategyTests.cs index f39fc59c97c..1fc6d37976e 100644 --- a/test/Polly.Extensions.Tests/Telemetry/TelemetryResilienceStrategyTests.cs +++ b/test/Polly.Extensions.Tests/Telemetry/TelemetryResilienceStrategyTests.cs @@ -23,7 +23,8 @@ public TelemetryResilienceStrategyTests() [Fact] public void Ctor_Ok() { - var duration = CreateStrategy().ExecutionDuration; + var strategy = CreateStrategy(); + var duration = ((TelemetryResilienceStrategy)strategy.GetType().GetRuntimeProperty("Strategy")!.GetValue(strategy)!).ExecutionDuration; duration.Unit.Should().Be("ms"); duration.Description.Should().Be("The execution duration and execution results of resilience strategies."); @@ -216,7 +217,12 @@ public void Execute_ExecutionHealth(bool healthy) } } - private TelemetryResilienceStrategy CreateStrategy() => new("my-builder", "my-instance", _loggerFactory, (_, r) => r, new List> { c => _enricher?.Invoke(c) }); + private ResilienceStrategy CreateStrategy() + { + return new CompositeStrategyBuilder() + .AddStrategy(_ => new TelemetryResilienceStrategy("my-builder", "my-instance", _loggerFactory, (_, r) => r, new List> { c => _enricher?.Invoke(c) }), new TestResilienceStrategyOptions()) + .Build(); + } public void Dispose() { diff --git a/test/Polly.RateLimiting.Tests/Polly.RateLimiting.Tests.csproj b/test/Polly.RateLimiting.Tests/Polly.RateLimiting.Tests.csproj index eee95b966cc..2b2c7807796 100644 --- a/test/Polly.RateLimiting.Tests/Polly.RateLimiting.Tests.csproj +++ b/test/Polly.RateLimiting.Tests/Polly.RateLimiting.Tests.csproj @@ -14,5 +14,6 @@ + diff --git a/test/Polly.RateLimiting.Tests/RateLimiterCompositeStrategyBuilderExtensionsTests.cs b/test/Polly.RateLimiting.Tests/RateLimiterCompositeStrategyBuilderExtensionsTests.cs index c9d6689d4dc..ae9582e54f0 100644 --- a/test/Polly.RateLimiting.Tests/RateLimiterCompositeStrategyBuilderExtensionsTests.cs +++ b/test/Polly.RateLimiting.Tests/RateLimiterCompositeStrategyBuilderExtensionsTests.cs @@ -1,17 +1,18 @@ using System.ComponentModel.DataAnnotations; using System.Threading.RateLimiting; using NSubstitute; +using Polly.Testing; namespace Polly.RateLimiting.Tests; public class RateLimiterCompositeStrategyBuilderExtensionsTests { - public static readonly TheoryData>> Data = new() + public static readonly TheoryData> Data = new() { builder => { builder.AddConcurrencyLimiter(2, 2); - AssertConcurrencyLimiter(builder, hasEvents: false); + AssertRateLimiterStrategy(builder, strategy => strategy.Limiter.Limiter.Should().BeOfType()); }, builder => { @@ -22,21 +23,21 @@ public class RateLimiterCompositeStrategyBuilderExtensionsTests QueueLimit = 2 }); - AssertConcurrencyLimiter(builder, hasEvents: false); + AssertRateLimiterStrategy(builder, strategy => strategy.Limiter.Limiter.Should().BeOfType()); }, builder => { var expected = Substitute.For(); builder.AddRateLimiter(expected); - AssertRateLimiter(builder, hasEvents: false, limiter => limiter.Should().Be(expected)); + AssertRateLimiterStrategy(builder, strategy => strategy.Limiter.Limiter.Should().Be(expected)); } }; [MemberData(nameof(Data))] [Theory(Skip = "https://github.com/stryker-mutator/stryker-net/issues/2144")] - public void AddRateLimiter_Extensions_Ok(Action> configure) + public void AddRateLimiter_Extensions_Ok(Action configure) { - var builder = new CompositeStrategyBuilder(); + var builder = new CompositeStrategyBuilder(); configure(builder); @@ -60,13 +61,13 @@ public void AddConcurrencyLimiter_InvalidOptions_Throws() [Fact] public void AddRateLimiter_AllExtensions_Ok() { - foreach (var configure in Data.Select(v => v[0]).Cast>>()) + foreach (var configure in Data.Select(v => v[0]).Cast>()) { - var builder = new CompositeStrategyBuilder(); + var builder = new CompositeStrategyBuilder(); configure(builder); - GetResilienceStrategy(builder.Build()).Should().BeOfType(); + AssertRateLimiterStrategy(builder); } } @@ -85,8 +86,10 @@ public void AddRateLimiter_Ok() RateLimiter = ResilienceRateLimiter.Create(limiter) }) .Build() + .GetInnerStrategies().Strategies.Single() + .StrategyType .Should() - .BeOfType(); + .Be(); } [Fact] @@ -125,51 +128,37 @@ public void AddRateLimiter_Options_Ok() { RateLimiter = ResilienceRateLimiter.Create(Substitute.For()) }) - .Build(); - - strategy.Should().BeOfType(); + .Build() + .GetInnerStrategies().Strategies + .Single() + .StrategyType + .Should() + .Be(); } - private static void AssertRateLimiter(CompositeStrategyBuilder builder, bool hasEvents, Action? assertLimiter = null) + private static void AssertRateLimiterStrategy(CompositeStrategyBuilder builder, Action? assert = null, bool hasEvents = false) { - var strategy = GetResilienceStrategy(builder.Build()); - strategy.Limiter.Should().NotBeNull(); + ResilienceStrategy strategy = builder.Build(); + var limiterStrategy = GetResilienceStrategy(strategy); + assert?.Invoke(limiterStrategy); if (hasEvents) { - strategy.OnLeaseRejected.Should().NotBeNull(); - strategy + limiterStrategy.OnLeaseRejected.Should().NotBeNull(); + limiterStrategy .OnLeaseRejected!(new OnRateLimiterRejectedArguments(ResilienceContextPool.Shared.Get(), Substitute.For(), null)) .Preserve().GetAwaiter().GetResult(); } else { - strategy.OnLeaseRejected.Should().BeNull(); + limiterStrategy.OnLeaseRejected.Should().BeNull(); } - assertLimiter?.Invoke(strategy.Limiter.Limiter!); - } - - private static void AssertConcurrencyLimiter(CompositeStrategyBuilder builder, bool hasEvents) - { - var strategy = GetResilienceStrategy(builder.Build()); - strategy.Limiter.Limiter.Should().BeOfType(); - - if (hasEvents) - { - strategy.OnLeaseRejected.Should().NotBeNull(); - strategy - .OnLeaseRejected!(new OnRateLimiterRejectedArguments(ResilienceContextPool.Shared.Get(), Substitute.For(), null)) - .Preserve().GetAwaiter().GetResult(); - } - else - { - strategy.OnLeaseRejected.Should().BeNull(); - } + strategy.GetInnerStrategies().Strategies.Single().StrategyType.Should().Be(typeof(RateLimiterResilienceStrategy)); } - private static RateLimiterResilienceStrategy GetResilienceStrategy(ResilienceStrategy strategy) + private static RateLimiterResilienceStrategy GetResilienceStrategy(ResilienceStrategy strategy) { - return (RateLimiterResilienceStrategy)strategy.GetType().GetProperty("Strategy", BindingFlags.Instance | BindingFlags.NonPublic)!.GetValue(strategy)!; + return (RateLimiterResilienceStrategy)strategy.GetType().GetRuntimeProperty("Strategy")!.GetValue(strategy)!; } } diff --git a/test/Polly.RateLimiting.Tests/RateLimiterResilienceStrategyTests.cs b/test/Polly.RateLimiting.Tests/RateLimiterResilienceStrategyTests.cs index 1dae568a25e..c65e9351b51 100644 --- a/test/Polly.RateLimiting.Tests/RateLimiterResilienceStrategyTests.cs +++ b/test/Polly.RateLimiting.Tests/RateLimiterResilienceStrategyTests.cs @@ -99,19 +99,18 @@ private void SetupLimiter(CancellationToken token) .Returns(result); } - private RateLimiterResilienceStrategy Create() + private ResilienceStrategy Create() { var builder = new CompositeStrategyBuilder { DiagnosticSource = _diagnosticSource }; - return (RateLimiterResilienceStrategy)builder - .AddRateLimiter(new RateLimiterStrategyOptions - { - RateLimiter = ResilienceRateLimiter.Create(_limiter), - OnRejected = _event - }) - .Build(); + return builder.AddRateLimiter(new RateLimiterStrategyOptions + { + RateLimiter = ResilienceRateLimiter.Create(_limiter), + OnRejected = _event + }) + .Build(); } } diff --git a/test/Polly.Specs/ResilienceStrategyConversionExtensionsTests.cs b/test/Polly.Specs/ResilienceStrategyConversionExtensionsTests.cs index 685acee397a..e153d914436 100644 --- a/test/Polly.Specs/ResilienceStrategyConversionExtensionsTests.cs +++ b/test/Polly.Specs/ResilienceStrategyConversionExtensionsTests.cs @@ -50,7 +50,7 @@ public void AsSyncPolicy_Ok() [Incoming.Key] = "incoming-value" }; - _strategy.AsSyncPolicy().Execute(_ => + _strategy.AsStrategy().AsSyncPolicy().Execute(_ => { context[Executing.Key] = "executing-value"; }, @@ -86,7 +86,7 @@ public void AsSyncPolicy_Result_Ok() [Incoming.Key] = "incoming-value" }; - var result = _strategy.AsSyncPolicy().Execute(_ => { context[Executing.Key] = "executing-value"; return "dummy"; }, context); + var result = _strategy.AsStrategy().AsSyncPolicy().Execute(_ => { context[Executing.Key] = "executing-value"; return "dummy"; }, context); AssertContext(context); result.Should().Be("dummy"); @@ -102,7 +102,7 @@ public async Task AsAsyncPolicy_Ok() [Incoming.Key] = "incoming-value" }; - await _strategy.AsAsyncPolicy().ExecuteAsync(_ => + await _strategy.AsStrategy().AsAsyncPolicy().ExecuteAsync(_ => { context[Executing.Key] = "executing-value"; return Task.CompletedTask; @@ -144,7 +144,7 @@ public async Task AsAsyncPolicy_Result_Ok() [Incoming.Key] = "incoming-value" }; - var result = await _strategy.AsAsyncPolicy().ExecuteAsync(_ => + var result = await _strategy.AsStrategy().AsAsyncPolicy().ExecuteAsync(_ => { context[Executing.Key] = "executing-value"; return Task.FromResult("dummy"); diff --git a/test/Polly.TestUtils/NonReactiveStrategyExtensions.cs b/test/Polly.TestUtils/NonReactiveStrategyExtensions.cs new file mode 100644 index 00000000000..c1c2f6786b9 --- /dev/null +++ b/test/Polly.TestUtils/NonReactiveStrategyExtensions.cs @@ -0,0 +1,14 @@ +using Polly.Utils; + +namespace Polly.TestUtils; + +public static class NonReactiveStrategyExtensions +{ + public static ResilienceStrategy AsStrategy(this NonReactiveResilienceStrategy strategy) => new NonReactiveResilienceStrategyBridge(strategy); + + public static TBuilder AddStrategy(this TBuilder builder, NonReactiveResilienceStrategy strategy) + where TBuilder : CompositeStrategyBuilderBase + { + return builder.AddStrategy(strategy.AsStrategy()); + } +} diff --git a/test/Polly.TestUtils/TestResilienceStrategy.TResult.cs b/test/Polly.TestUtils/TestResilienceStrategy.TResult.cs index 35a443a57ff..12ed008e15a 100644 --- a/test/Polly.TestUtils/TestResilienceStrategy.TResult.cs +++ b/test/Polly.TestUtils/TestResilienceStrategy.TResult.cs @@ -3,7 +3,7 @@ namespace Polly.TestUtils; public class TestResilienceStrategy : ResilienceStrategy { public TestResilienceStrategy() - : base(new TestResilienceStrategy()) + : base(new TestResilienceStrategy().AsStrategy()) { } } diff --git a/test/Polly.TestUtils/TestResilienceStrategy.cs b/test/Polly.TestUtils/TestResilienceStrategy.cs index 56629a3d7a9..f84f0201a86 100644 --- a/test/Polly.TestUtils/TestResilienceStrategy.cs +++ b/test/Polly.TestUtils/TestResilienceStrategy.cs @@ -1,6 +1,6 @@ namespace Polly.TestUtils; -public class TestResilienceStrategy : ResilienceStrategy +public class TestResilienceStrategy : NonReactiveResilienceStrategy { public Action? Before { get; set; } diff --git a/test/Polly.Testing.Tests/ResilienceStrategyExtensionsTests.cs b/test/Polly.Testing.Tests/ResilienceStrategyExtensionsTests.cs index 65f6016bfa2..baf4f8c12cd 100644 --- a/test/Polly.Testing.Tests/ResilienceStrategyExtensionsTests.cs +++ b/test/Polly.Testing.Tests/ResilienceStrategyExtensionsTests.cs @@ -25,7 +25,7 @@ public void GetInnerStrategies_Generic_Ok() .AddTimeout(TimeSpan.FromSeconds(1)) .AddHedging(new()) .AddConcurrencyLimiter(10) - .AddStrategy(new CustomStrategy()) + .AddStrategy(_ => new CustomStrategy(), new TestOptions()) .ConfigureTelemetry(NullLoggerFactory.Instance) .Build(); @@ -65,7 +65,7 @@ public void GetInnerStrategies_NonGeneric_Ok() .AddCircuitBreaker(new()) .AddTimeout(TimeSpan.FromSeconds(1)) .AddConcurrencyLimiter(10) - .AddStrategy(new CustomStrategy()) + .AddStrategy(_ => new CustomStrategy(), new TestOptions()) .ConfigureTelemetry(NullLoggerFactory.Instance) .Build(); @@ -120,7 +120,7 @@ public void GetInnerStrategies_Reloadable_Ok() builder .AddConcurrencyLimiter(10) - .AddStrategy(new CustomStrategy()); + .AddStrategy(_ => new CustomStrategy(), new TestOptions()); }); // act @@ -134,9 +134,13 @@ public void GetInnerStrategies_Reloadable_Ok() descriptor.Strategies[1].StrategyType.Should().Be(typeof(CustomStrategy)); } - private sealed class CustomStrategy : ResilienceStrategy + private sealed class CustomStrategy : NonReactiveResilienceStrategy { protected override ValueTask> ExecuteCore(Func>> callback, ResilienceContext context, TState state) => throw new NotSupportedException(); } + + private class TestOptions : ResilienceStrategyOptions + { + } } From 73dd6d61a72c630e914bcdb5c8f1abcf52b0e180 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Aug 2023 17:20:57 +0000 Subject: [PATCH 4/8] Bump github/codeql-action from 2.21.2 to 2.21.3 (#1477) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.21.2 to 2.21.3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/0ba4244466797eb048eb91a6cd43d5c03ca8bd05...5b6282e01c62d02e720b81eb8a51204f527c3624) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/ossf-scorecard.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index bde99a8890e..3a16843a0bf 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -41,14 +41,14 @@ jobs: restore-keys: ${{ runner.os }}-nuget- - name: Initialize CodeQL - uses: github/codeql-action/init@0ba4244466797eb048eb91a6cd43d5c03ca8bd05 # v2.21.2 + uses: github/codeql-action/init@5b6282e01c62d02e720b81eb8a51204f527c3624 # v2.21.3 with: languages: ${{ matrix.language }} - name: Autobuild - uses: github/codeql-action/autobuild@0ba4244466797eb048eb91a6cd43d5c03ca8bd05 # v2.21.2 + uses: github/codeql-action/autobuild@5b6282e01c62d02e720b81eb8a51204f527c3624 # v2.21.3 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@0ba4244466797eb048eb91a6cd43d5c03ca8bd05 # v2.21.2 + uses: github/codeql-action/analyze@5b6282e01c62d02e720b81eb8a51204f527c3624 # v2.21.3 with: category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 0670667973a..530b0eff1ba 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -39,6 +39,6 @@ jobs: retention-days: 5 - name: Upload to code-scanning - uses: github/codeql-action/upload-sarif@0ba4244466797eb048eb91a6cd43d5c03ca8bd05 # v2.21.2 + uses: github/codeql-action/upload-sarif@5b6282e01c62d02e720b81eb8a51204f527c3624 # v2.21.3 with: sarif_file: results.sarif From 334329614965990ebf0ae60d55c5325aa5075ec5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Aug 2023 17:22:05 +0000 Subject: [PATCH 5/8] Bump BenchmarkDotNet from 0.13.6 to 0.13.7 (#1480) Bumps [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) from 0.13.6 to 0.13.7. - [Release notes](https://github.com/dotnet/BenchmarkDotNet/releases) - [Commits](https://github.com/dotnet/BenchmarkDotNet/compare/v0.13.6...v0.13.7) --- updated-dependencies: - dependency-name: BenchmarkDotNet dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index ce5b24e4b09..c67025a6b01 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -6,7 +6,7 @@ - + From 473277a32f7f0b9d32dc947113b2e9c2ac1abcd8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Aug 2023 17:22:29 +0000 Subject: [PATCH 6/8] Bump actions/dependency-review-action from 3.0.6 to 3.0.7 (#1478) Bumps [actions/dependency-review-action](https://github.com/actions/dependency-review-action) from 3.0.6 to 3.0.7. - [Release notes](https://github.com/actions/dependency-review-action/releases) - [Commits](https://github.com/actions/dependency-review-action/compare/1360a344ccb0ab6e9475edef90ad2f46bf8003b1...7d90b4f05fea31dde1c4a1fb3fa787e197ea93ab) --- updated-dependencies: - dependency-name: actions/dependency-review-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/dependency-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 37900cf0c05..8712a520d7b 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -18,4 +18,4 @@ jobs: uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Review dependencies - uses: actions/dependency-review-action@1360a344ccb0ab6e9475edef90ad2f46bf8003b1 # v3.0.6 + uses: actions/dependency-review-action@7d90b4f05fea31dde1c4a1fb3fa787e197ea93ab # v3.0.7 From 7c126895b4ecba9682610fa58b0ad664e81842f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Aug 2023 17:25:49 +0000 Subject: [PATCH 7/8] Bump Microsoft.NET.Test.Sdk from 17.6.3 to 17.7.0 (#1481) Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.6.3 to 17.7.0. - [Release notes](https://github.com/microsoft/vstest/releases) - [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md) - [Commits](https://github.com/microsoft/vstest/compare/v17.6.3...v17.7.0) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index c67025a6b01..d22887fc6f0 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -19,7 +19,7 @@ - + From da345d05a69c2417bd635ae60a561e34710fb22f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Aug 2023 17:43:47 +0000 Subject: [PATCH 8/8] Bump SonarAnalyzer.CSharp from 9.6.0.74858 to 9.7.0.75501 (#1479) * Bump SonarAnalyzer.CSharp from 9.6.0.74858 to 9.7.0.75501 Bumps [SonarAnalyzer.CSharp](https://github.com/SonarSource/sonar-dotnet) from 9.6.0.74858 to 9.7.0.75501. - [Release notes](https://github.com/SonarSource/sonar-dotnet/releases) - [Commits](https://github.com/SonarSource/sonar-dotnet/compare/9.6.0.74858...9.7.0.75501) --- updated-dependencies: - dependency-name: SonarAnalyzer.CSharp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Try fixing warning Try invert condition rather than `is false`. --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Martin Costello --- Directory.Packages.props | 2 +- .../CircuitBreaker/Controller/CircuitStateController.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index d22887fc6f0..00b74aefa69 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -28,7 +28,7 @@ - + diff --git a/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs b/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs index fbce5c0d005..1ce6bd9c69b 100644 --- a/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs +++ b/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs @@ -135,7 +135,7 @@ public ValueTask CloseCircuitAsync(ResilienceContext context) exception = _circuitState switch { CircuitState.Open => _breakingException, - CircuitState.HalfOpen when isHalfOpen is false => _breakingException, + CircuitState.HalfOpen when !isHalfOpen => _breakingException, CircuitState.Isolated => new IsolatedCircuitException(), _ => null };