diff --git a/src/Polly.Core/PublicAPI.Unshipped.txt b/src/Polly.Core/PublicAPI.Unshipped.txt index 8e29facb138..b150181940e 100644 --- a/src/Polly.Core/PublicAPI.Unshipped.txt +++ b/src/Polly.Core/PublicAPI.Unshipped.txt @@ -365,8 +365,7 @@ Polly.Telemetry.TelemetryEventArguments.Outcome.get -> Polly.Outcome? Polly.Telemetry.TelemetryEventArguments.Source.get -> Polly.Telemetry.ResilienceTelemetrySource! Polly.Timeout.OnTimeoutArguments Polly.Timeout.OnTimeoutArguments.Context.get -> Polly.ResilienceContext! -Polly.Timeout.OnTimeoutArguments.Exception.get -> System.Exception! -Polly.Timeout.OnTimeoutArguments.OnTimeoutArguments(Polly.ResilienceContext! context, System.Exception! exception, System.TimeSpan timeout) -> void +Polly.Timeout.OnTimeoutArguments.OnTimeoutArguments(Polly.ResilienceContext! context, System.TimeSpan timeout) -> void Polly.Timeout.OnTimeoutArguments.Timeout.get -> System.TimeSpan Polly.Timeout.TimeoutGeneratorArguments Polly.Timeout.TimeoutGeneratorArguments.Context.get -> Polly.ResilienceContext! diff --git a/src/Polly.Core/Timeout/OnTimeoutArguments.cs b/src/Polly.Core/Timeout/OnTimeoutArguments.cs index c78eb02f676..b235bf724a9 100644 --- a/src/Polly.Core/Timeout/OnTimeoutArguments.cs +++ b/src/Polly.Core/Timeout/OnTimeoutArguments.cs @@ -9,12 +9,10 @@ public sealed class OnTimeoutArguments /// Initializes a new instance of the class. /// /// The context associated with the execution of a user-provided callback. - /// The original exception that caused the timeout. /// The timeout value assigned. - public OnTimeoutArguments(ResilienceContext context, Exception exception, TimeSpan timeout) + public OnTimeoutArguments(ResilienceContext context, TimeSpan timeout) { Context = context; - Exception = exception; Timeout = timeout; } @@ -23,11 +21,6 @@ public OnTimeoutArguments(ResilienceContext context, Exception exception, TimeSp /// public ResilienceContext Context { get; } - /// - /// Gets hte original exception that caused the timeout. - /// - public Exception Exception { get; } - /// /// Gets the timeout value assigned. /// diff --git a/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs b/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs index d0252768a62..0fb5cac03bf 100644 --- a/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs +++ b/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs @@ -59,7 +59,7 @@ protected internal override async ValueTask> ExecuteCore Polly.RateLimit Polly.RateLimiting.RateLimiterStrategyOptions.RateLimiter.set -> void Polly.RateLimiting.RateLimiterStrategyOptions.RateLimiterStrategyOptions() -> void Polly.RateLimiting.ResilienceRateLimiter +Polly.RateLimiting.ResilienceRateLimiter.Dispose() -> void +Polly.RateLimiting.ResilienceRateLimiter.DisposeAsync() -> System.Threading.Tasks.ValueTask static Polly.RateLimiterCompositeStrategyBuilderExtensions.AddConcurrencyLimiter(this TBuilder! builder, int permitLimit, int queueLimit = 0) -> TBuilder! static Polly.RateLimiterCompositeStrategyBuilderExtensions.AddConcurrencyLimiter(this TBuilder! builder, System.Threading.RateLimiting.ConcurrencyLimiterOptions! options) -> TBuilder! static Polly.RateLimiterCompositeStrategyBuilderExtensions.AddRateLimiter(this TBuilder! builder, Polly.RateLimiting.RateLimiterStrategyOptions! options) -> TBuilder! diff --git a/src/Polly.RateLimiting/ResilienceRateLimiter.cs b/src/Polly.RateLimiting/ResilienceRateLimiter.cs index 629694a58c2..a3f79ef2580 100644 --- a/src/Polly.RateLimiting/ResilienceRateLimiter.cs +++ b/src/Polly.RateLimiting/ResilienceRateLimiter.cs @@ -5,7 +5,7 @@ namespace Polly.RateLimiting; /// /// This class is just a simple adapter for the built-in limiters in the System.Threading.RateLimiting namespace. /// -public sealed class ResilienceRateLimiter +public sealed class ResilienceRateLimiter : IDisposable, IAsyncDisposable { private ResilienceRateLimiter(RateLimiter? limiter, PartitionedRateLimiter? partitionedLimiter) { @@ -42,4 +42,30 @@ internal ValueTask AcquireAsync(ResilienceContext context) return Limiter!.AcquireAsync(permitCount: 1, context.CancellationToken); } } + + /// + public ValueTask DisposeAsync() + { + if (PartitionedLimiter is not null) + { + return PartitionedLimiter.DisposeAsync(); + } + else + { + return Limiter!.DisposeAsync(); + } + } + + /// + public void Dispose() + { + if (PartitionedLimiter is not null) + { + PartitionedLimiter.Dispose(); + } + else + { + Limiter!.Dispose(); + } + } } diff --git a/test/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyTests.cs b/test/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyTests.cs index 20f07e0684a..435354b0ef4 100644 --- a/test/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyTests.cs +++ b/test/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyTests.cs @@ -60,7 +60,6 @@ public async Task Execute_EnsureOnTimeoutCalled() var executionTime = _delay + TimeSpan.FromSeconds(1); _options.OnTimeout = args => { - args.Exception.Should().BeAssignableTo(); args.Timeout.Should().Be(_delay); args.Context.Should().NotBeNull(); args.Context.CancellationToken.IsCancellationRequested.Should().BeFalse(); diff --git a/test/Polly.Core.Tests/Timeout/TimeoutTestUtils.cs b/test/Polly.Core.Tests/Timeout/TimeoutTestUtils.cs index 7d572a70143..302cdc8be4a 100644 --- a/test/Polly.Core.Tests/Timeout/TimeoutTestUtils.cs +++ b/test/Polly.Core.Tests/Timeout/TimeoutTestUtils.cs @@ -4,7 +4,7 @@ namespace Polly.Core.Tests.Timeout; public static class TimeoutTestUtils { - public static OnTimeoutArguments OnTimeoutArguments() => new(ResilienceContextPool.Shared.Get(), new InvalidOperationException(), TimeSpan.FromSeconds(1)); + public static OnTimeoutArguments OnTimeoutArguments() => new(ResilienceContextPool.Shared.Get(), TimeSpan.FromSeconds(1)); public static TimeoutGeneratorArguments TimeoutGeneratorArguments() => new(ResilienceContextPool.Shared.Get()); diff --git a/test/Polly.RateLimiting.Tests/ResilienceRateLimiterTests.cs b/test/Polly.RateLimiting.Tests/ResilienceRateLimiterTests.cs index 439657c6620..428e89f7b8a 100644 --- a/test/Polly.RateLimiting.Tests/ResilienceRateLimiterTests.cs +++ b/test/Polly.RateLimiting.Tests/ResilienceRateLimiterTests.cs @@ -18,7 +18,7 @@ public async Task Create_RateLimiter_Ok() .Invoke(rateLimiter, new object[] { 1, default(CancellationToken) }) .Returns(leaseTask); - var limiter = ResilienceRateLimiter.Create(rateLimiter); + using var limiter = ResilienceRateLimiter.Create(rateLimiter); (await limiter.AcquireAsync(ResilienceContextPool.Shared.Get())).Should().Be(lease); limiter.Limiter.Should().NotBeNull(); @@ -39,9 +39,51 @@ public async Task Create_PartitionedRateLimiter_Ok() .Invoke(rateLimiter, new object[] { context, 1, default(CancellationToken) }) .Returns(leaseTask); - var limiter = ResilienceRateLimiter.Create(rateLimiter); + using var limiter = ResilienceRateLimiter.Create(rateLimiter); (await limiter.AcquireAsync(context)).Should().Be(lease); limiter.PartitionedLimiter.Should().NotBeNull(); } + + [InlineData(true)] + [InlineData(false)] + [Theory] + public async Task RateLimiter_Dispose_EnsureDisposed(bool isAsync) + { + using var concurrencyLimiter = new ConcurrencyLimiter(new ConcurrencyLimiterOptions { PermitLimit = 10, QueueLimit = 10 }); + var limiter = ResilienceRateLimiter.Create(concurrencyLimiter); + + if (isAsync) + { + await limiter.DisposeAsync(); + } + else + { + limiter.Dispose(); + } + + await limiter.Invoking(l => l.AcquireAsync(ResilienceContextPool.Shared.Get()).AsTask()).Should().ThrowAsync(); + } + + [InlineData(true)] + [InlineData(false)] + [Theory] + public async Task PartitionedRateLimiter_Dispose_EnsureDisposed(bool isAsync) + { + using var partitioned = PartitionedRateLimiter.Create( + c => RateLimitPartition.GetConcurrencyLimiter("a", + _ => new ConcurrencyLimiterOptions { PermitLimit = 10, QueueLimit = 10 })); + var limiter = ResilienceRateLimiter.Create(partitioned); + + if (isAsync) + { + await limiter.DisposeAsync(); + } + else + { + limiter.Dispose(); + } + + await limiter.Invoking(l => l.AcquireAsync(ResilienceContextPool.Shared.Get()).AsTask()).Should().ThrowAsync(); + } }