Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/Polly.Core/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,7 @@ Polly.Telemetry.TelemetryEventArguments.Outcome.get -> Polly.Outcome<object!>?
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!
Expand Down
9 changes: 1 addition & 8 deletions src/Polly.Core/Timeout/OnTimeoutArguments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,10 @@ public sealed class OnTimeoutArguments
/// Initializes a new instance of the <see cref="OnTimeoutArguments"/> class.
/// </summary>
/// <param name="context">The context associated with the execution of a user-provided callback.</param>
/// <param name="exception">The original exception that caused the timeout.</param>
/// <param name="timeout">The timeout value assigned.</param>
public OnTimeoutArguments(ResilienceContext context, Exception exception, TimeSpan timeout)
public OnTimeoutArguments(ResilienceContext context, TimeSpan timeout)
{
Context = context;
Exception = exception;
Timeout = timeout;
}

Expand All @@ -23,11 +21,6 @@ public OnTimeoutArguments(ResilienceContext context, Exception exception, TimeSp
/// </summary>
public ResilienceContext Context { get; }

/// <summary>
/// Gets hte original exception that caused the timeout.
/// </summary>
public Exception Exception { get; }

/// <summary>
/// Gets the timeout value assigned.
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ protected internal override async ValueTask<Outcome<TResult>> ExecuteCore<TResul
// check the outcome
if (isCancellationRequested && outcome.Exception is OperationCanceledException e && !previousToken.IsCancellationRequested)
{
var args = new OnTimeoutArguments(context, e, timeout);
var args = new OnTimeoutArguments(context, timeout);
_telemetry.Report(new(ResilienceEventSeverity.Error, TimeoutConstants.OnTimeoutEvent), context, args);

if (OnTimeout is not null)
Expand Down
2 changes: 2 additions & 0 deletions src/Polly.RateLimiting/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ Polly.RateLimiting.RateLimiterStrategyOptions.RateLimiter.get -> 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<TBuilder>(this TBuilder! builder, int permitLimit, int queueLimit = 0) -> TBuilder!
static Polly.RateLimiterCompositeStrategyBuilderExtensions.AddConcurrencyLimiter<TBuilder>(this TBuilder! builder, System.Threading.RateLimiting.ConcurrencyLimiterOptions! options) -> TBuilder!
static Polly.RateLimiterCompositeStrategyBuilderExtensions.AddRateLimiter<TBuilder>(this TBuilder! builder, Polly.RateLimiting.RateLimiterStrategyOptions! options) -> TBuilder!
Expand Down
28 changes: 27 additions & 1 deletion src/Polly.RateLimiting/ResilienceRateLimiter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Polly.RateLimiting;
/// <summary>
/// This class is just a simple adapter for the built-in limiters in the <c>System.Threading.RateLimiting</c> namespace.
/// </summary>
public sealed class ResilienceRateLimiter
public sealed class ResilienceRateLimiter : IDisposable, IAsyncDisposable
{
private ResilienceRateLimiter(RateLimiter? limiter, PartitionedRateLimiter<ResilienceContext>? partitionedLimiter)
{
Expand Down Expand Up @@ -42,4 +42,30 @@ internal ValueTask<RateLimitLease> AcquireAsync(ResilienceContext context)
return Limiter!.AcquireAsync(permitCount: 1, context.CancellationToken);
}
}

/// <inheritdoc/>
public ValueTask DisposeAsync()
{
if (PartitionedLimiter is not null)
{
return PartitionedLimiter.DisposeAsync();
}
else
{
return Limiter!.DisposeAsync();
}
}

/// <inheritdoc/>
public void Dispose()
{
if (PartitionedLimiter is not null)
{
PartitionedLimiter.Dispose();
}
else
{
Limiter!.Dispose();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ public async Task Execute_EnsureOnTimeoutCalled()
var executionTime = _delay + TimeSpan.FromSeconds(1);
_options.OnTimeout = args =>
{
args.Exception.Should().BeAssignableTo<OperationCanceledException>();
args.Timeout.Should().Be(_delay);
args.Context.Should().NotBeNull();
args.Context.CancellationToken.IsCancellationRequested.Should().BeFalse();
Expand Down
2 changes: 1 addition & 1 deletion test/Polly.Core.Tests/Timeout/TimeoutTestUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand Down
46 changes: 44 additions & 2 deletions test/Polly.RateLimiting.Tests/ResilienceRateLimiterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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<ObjectDisposedException>();
}

[InlineData(true)]
[InlineData(false)]
[Theory]
public async Task PartitionedRateLimiter_Dispose_EnsureDisposed(bool isAsync)
{
using var partitioned = PartitionedRateLimiter.Create<ResilienceContext, string>(
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<ObjectDisposedException>();
}
}