Skip to content

Commit

Permalink
Merge branch 'main' into fix-warning-S3215
Browse files Browse the repository at this point in the history
  • Loading branch information
baranyaimate authored Jul 14, 2024
2 parents 66b7118 + 1eb48d7 commit 67efc69
Show file tree
Hide file tree
Showing 12 changed files with 118 additions and 8 deletions.
2 changes: 1 addition & 1 deletion src/Polly/Polly.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<ProjectType>Library</ProjectType>
<MutationScore>70</MutationScore>
<IncludePollyUsings>true</IncludePollyUsings>
<NoWarn>$(NoWarn);CA1010;CA1031;CA1032;CA1051;CA1062;CA1063;CA1064;CA1710;CA1716;CA1724;CA1805;CA1815;CA1816;CA2211</NoWarn>
<NoWarn>$(NoWarn);CA1010;CA1031;CA1051;CA1062;CA1063;CA1064;CA1710;CA1716;CA1724;CA1805;CA1815;CA1816;CA2211</NoWarn>
<NoWarn>$(NoWarn);S2223;S3246;S3971;S4039;S4457</NoWarn>
<!--Public API Analyzers: We do not need to fix these as it would break compatibility with released Polly versions-->
<NoWarn>$(NoWarn);RS0037;</NoWarn>
Expand Down
27 changes: 27 additions & 0 deletions src/Polly/RateLimit/RateLimitRejectedException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,33 @@ public class RateLimitRejectedException : ExecutionRejectedException
/// </summary>
public TimeSpan RetryAfter { get; private set; }

/// <summary>
/// Initializes a new instance of the <see cref="RateLimitRejectedException"/> class.
/// </summary>
public RateLimitRejectedException()
: base("The operation could not be executed because it was rejected by the rate limit.")
{
}

/// <summary>
/// Initializes a new instance of the <see cref="RateLimitRejectedException"/> class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public RateLimitRejectedException(string message)
: base(message)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="RateLimitRejectedException"/> class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
/// <param name="inner">The inner exception.</param>
public RateLimitRejectedException(string message, Exception inner)
: base(message, inner)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="RateLimitRejectedException"/> class.
/// </summary>
Expand Down
22 changes: 22 additions & 0 deletions src/Polly/Utilities/TimedLock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,30 @@ private class Sentinel

internal class LockTimeoutException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="LockTimeoutException"/> class.
/// </summary>
public LockTimeoutException()
: base("Timeout waiting for lock")
{
}

/// <summary>
/// Initializes a new instance of the <see cref="LockTimeoutException"/> class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public LockTimeoutException(string message)
: base(message)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="LockTimeoutException"/> class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
/// <param name="innerException">The inner exception.</param>
public LockTimeoutException(string message, Exception innerException)
: base(message, innerException)
{
}
}
5 changes: 4 additions & 1 deletion test/Polly.Specs/Helpers/Bulkhead/TraceableAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ public Task ExecuteOnBulkhead<TResult>(BulkheadPolicy<TResult?> bulkhead) =>
return default;
}, _cancellationSource.Token));

// Note re TaskCreationOptions.LongRunning: Testing the parallelization of the bulkhead policy efficiently requires the ability to start large numbers of parallel tasks in a short space of time. The ThreadPool's algorithm of only injecting extra threads (when necessary) at a rate of two-per-second however makes high-volume tests using the ThreadPool both slow and flaky. For PCL tests further, ThreadPool.SetMinThreads(...) is not available, to mitigate this. Using TaskCreationOptions.LongRunning allows us to force tasks to be started near-instantly on non-ThreadPool threads.
// Note re TaskCreationOptions.LongRunning: Testing the parallelization of the bulkhead policy efficiently requires the ability to start large numbers of parallel tasks in a short space of time.
// The ThreadPool's algorithm of only injecting extra threads (when necessary) at a rate of two-per-second however makes high-volume tests using the ThreadPool both slow and flaky.
// For PCL tests further, ThreadPool.SetMinThreads(...) is not available, to mitigate this.
// Using TaskCreationOptions.LongRunning allows us to force tasks to be started near-instantly on non-ThreadPool threads.
private Task ExecuteThroughSyncBulkheadOuter(Action executeThroughBulkheadInner)
{
if (Status != TraceableActionStatus.Unstarted)
Expand Down
2 changes: 1 addition & 1 deletion test/Polly.Specs/Polly.Specs.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<Include>[Polly]*</Include>
<IncludePollyUsings>true</IncludePollyUsings>
<NoWarn>$(NoWarn);CA1030;CA1031;CA2008;CA2201</NoWarn>
<NoWarn>$(NoWarn);S103;S104;S2184;S6966</NoWarn>
<NoWarn>$(NoWarn);S104;S2184;S6966</NoWarn>
<NoWarn>$(NoWarn);SA1204;SA1402;SA1600</NoWarn>
</PropertyGroup>

Expand Down
35 changes: 35 additions & 0 deletions test/Polly.Specs/RateLimit/RateLimitRejectedExceptionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
namespace Polly.Specs.RateLimit;

public class RateLimitRejectedExceptionTests
{
[Fact]
public void Ctor_Ok()
{
const string Dummy = "dummy";
var exception = new InvalidOperationException();
var retryAfter = TimeSpan.FromSeconds(4);

new RateLimitRejectedException().Message.Should().Be("The operation could not be executed because it was rejected by the rate limit.");
new RateLimitRejectedException(Dummy).Message.Should().Be(Dummy);

var rate = new RateLimitRejectedException(Dummy, exception);
rate.Message.Should().Be(Dummy);
rate.InnerException.Should().Be(exception);

new RateLimitRejectedException(retryAfter).RetryAfter.Should().Be(retryAfter);
new RateLimitRejectedException(retryAfter).Message.Should().Be($"The operation has been rate-limited and should be retried after {retryAfter}");

rate = new RateLimitRejectedException(retryAfter, exception);
rate.RetryAfter.Should().Be(retryAfter);
rate.InnerException.Should().Be(exception);

rate = new RateLimitRejectedException(retryAfter, Dummy);
rate.RetryAfter.Should().Be(retryAfter);
rate.Message.Should().Be(Dummy);

rate = new RateLimitRejectedException(retryAfter, Dummy, exception);
rate.RetryAfter.Should().Be(retryAfter);
rate.Message.Should().Be(Dummy);
rate.InnerException.Should().Be(exception);
}
}
3 changes: 2 additions & 1 deletion test/Polly.Specs/Retry/RetryAsyncSpecs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,8 @@ await policy.Awaiting(x => x.RaiseExceptionAsync<DivideByZeroException>())
public async Task Should_wait_asynchronously_for_async_onretry_delegate()
{
// This test relates to https://github.com/App-vNext/Polly/issues/107.
// An async (...) => { ... } anonymous delegate with no return type may compile to either an async void or an async Task method; which assign to an Action<...> or Func<..., Task> respectively. However, if it compiles to async void (assigning to Action<...>), then the delegate, when run, will return at the first await, and execution continues without waiting for the Action to complete, as described by Stephen Toub: https://devblogs.microsoft.com/pfxteam/potential-pitfalls-to-avoid-when-passing-around-async-lambdas/
// An async (...) => { ... } anonymous delegate with no return type may compile to either an async void or an async Task method; which assign to an Action<...> or Func<..., Task> respectively.
// However, if it compiles to async void (assigning to Action<...>), then the delegate, when run, will return at the first await, and execution continues without waiting for the Action to complete, as described by Stephen Toub: https://devblogs.microsoft.com/pfxteam/potential-pitfalls-to-avoid-when-passing-around-async-lambdas/
// If Polly were to declare only an Action<...> delegate for onRetry - but users declared async () => { } onRetry delegates - the compiler would happily assign them to the Action<...>, but the next 'try' of the retry policy would/could occur before onRetry execution had completed.
// This test ensures the relevant retry policy does have a Func<..., Task> form for onRetry, and that it is awaited before the next try commences.

Expand Down
3 changes: 2 additions & 1 deletion test/Polly.Specs/Retry/RetryForeverAsyncSpecs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,8 @@ await policy.Awaiting(x => x.RaiseExceptionAsync<ArgumentException>())
public async Task Should_wait_asynchronously_for_async_onretry_delegate()
{
// This test relates to https://github.com/App-vNext/Polly/issues/107.
// An async (...) => { ... } anonymous delegate with no return type may compile to either an async void or an async Task method; which assign to an Action<...> or Func<..., Task> respectively. However, if it compiles to async void (assigning tp Action<...>), then the delegate, when run, will return at the first await, and execution continues without waiting for the Action to complete, as described by Stephen Toub: https://devblogs.microsoft.com/pfxteam/potential-pitfalls-to-avoid-when-passing-around-async-lambdas/
// An async (...) => { ... } anonymous delegate with no return type may compile to either an async void or an async Task method; which assign to an Action<...> or Func<..., Task> respectively.
// However, if it compiles to async void (assigning tp Action<...>), then the delegate, when run, will return at the first await, and execution continues without waiting for the Action to complete, as described by Stephen Toub: https://devblogs.microsoft.com/pfxteam/potential-pitfalls-to-avoid-when-passing-around-async-lambdas/
// If Polly were to declare only an Action<...> delegate for onRetry - but users declared async () => { } onRetry delegates - the compiler would happily assign them to the Action<...>, but the next 'try' would/could occur before onRetry execution had completed.
// This test ensures the relevant retry policy does have a Func<..., Task> form for onRetry, and that it is awaited before the next try commences.

Expand Down
3 changes: 2 additions & 1 deletion test/Polly.Specs/Retry/RetryTResultSpecsAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,8 @@ public async Task Should_not_call_onretry_when_retry_count_is_zero_with_context(
public async Task Should_wait_asynchronously_for_async_onretry_delegate()
{
// This test relates to https://github.com/App-vNext/Polly/issues/107.
// An async (...) => { ... } anonymous delegate with no return type may compile to either an async void or an async Task method; which assign to an Action<...> or Func<..., Task> respectively. However, if it compiles to async void (assigning tp Action<...>), then the delegate, when run, will return at the first await, and execution continues without waiting for the Action to complete, as described by Stephen Toub: https://devblogs.microsoft.com/pfxteam/potential-pitfalls-to-avoid-when-passing-around-async-lambdas/
// An async (...) => { ... } anonymous delegate with no return type may compile to either an async void or an async Task method; which assign to an Action<...> or Func<..., Task> respectively.
// However, if it compiles to async void (assigning tp Action<...>), then the delegate, when run, will return at the first await, and execution continues without waiting for the Action to complete, as described by Stephen Toub: https://devblogs.microsoft.com/pfxteam/potential-pitfalls-to-avoid-when-passing-around-async-lambdas/
// If Polly were to declare only an Action<...> delegate for onRetry - but users declared async () => { } onRetry delegates - the compiler would happily assign them to the Action<...>, but the next 'try' would/could occur before onRetry execution had completed.
// This test ensures the relevant retry policy does have a Func<..., Task> form for onRetry, and that it is awaited before the next try commences.

Expand Down
3 changes: 2 additions & 1 deletion test/Polly.Specs/Retry/WaitAndRetryAsyncSpecs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -708,7 +708,8 @@ await policy.Awaiting(x => x.RaiseExceptionAsync<DivideByZeroException>())
public async Task Should_wait_asynchronously_for_async_onretry_delegate()
{
// This test relates to https://github.com/App-vNext/Polly/issues/107.
// An async (...) => { ... } anonymous delegate with no return type may compile to either an async void or an async Task method; which assign to an Action<...> or Func<..., Task> respectively. However, if it compiles to async void (assigning tp Action<...>), then the delegate, when run, will return at the first await, and execution continues without waiting for the Action to complete, as described by Stephen Toub: https://devblogs.microsoft.com/pfxteam/potential-pitfalls-to-avoid-when-passing-around-async-lambdas/
// An async (...) => { ... } anonymous delegate with no return type may compile to either an async void or an async Task method; which assign to an Action<...> or Func<..., Task> respectively.
// However, if it compiles to async void (assigning tp Action<...>), then the delegate, when run, will return at the first await, and execution continues without waiting for the Action to complete, as described by Stephen Toub: https://devblogs.microsoft.com/pfxteam/potential-pitfalls-to-avoid-when-passing-around-async-lambdas/
// If Polly were to declare only an Action<...> delegate for onRetry - but users declared async () => { } onRetry delegates - the compiler would happily assign them to the Action<...>, but the next 'try' would/could occur before onRetry execution had completed.
// This test ensures the relevant retry policy does have a Func<..., Task> form for onRetry, and that it is awaited before the next try commences.

Expand Down
3 changes: 2 additions & 1 deletion test/Polly.Specs/Retry/WaitAndRetryForeverAsyncSpecs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,8 @@ await policy.ExecuteAsync(async (context, _) =>
public async Task Should_wait_asynchronously_for_async_onretry_delegate()
{
// This test relates to https://github.com/App-vNext/Polly/issues/107.
// An async (...) => { ... } anonymous delegate with no return type may compile to either an async void or an async Task method; which assign to an Action<...> or Func<..., Task> respectively. However, if it compiles to async void (assigning tp Action<...>), then the delegate, when run, will return at the first await, and execution continues without waiting for the Action to complete, as described by Stephen Toub: https://devblogs.microsoft.com/pfxteam/potential-pitfalls-to-avoid-when-passing-around-async-lambdas/
// An async (...) => { ... } anonymous delegate with no return type may compile to either an async void or an async Task method; which assign to an Action<...> or Func<..., Task> respectively.
// However, if it compiles to async void (assigning tp Action<...>), then the delegate, when run, will return at the first await, and execution continues without waiting for the Action to complete, as described by Stephen Toub: https://devblogs.microsoft.com/pfxteam/potential-pitfalls-to-avoid-when-passing-around-async-lambdas/
// If Polly were to declare only an Action<...> delegate for onRetry - but users declared async () => { } onRetry delegates - the compiler would happily assign them to the Action<...>, but the next 'try' would/could occur before onRetry execution had completed.
// This test ensures the relevant retry policy does have a Func<..., Task> form for onRetry, and that it is awaited before the next try commences.

Expand Down
18 changes: 18 additions & 0 deletions test/Polly.Specs/Utilities/LockTimeoutExceptionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace Polly.Specs.Utilities;

public class LockTimeoutExceptionTests
{
[Fact]
public void Ctor_Ok()
{
const string Dummy = "dummy";
var exception = new InvalidOperationException();

new LockTimeoutException().Message.Should().Be("Timeout waiting for lock");
new LockTimeoutException(Dummy).Message.Should().Be(Dummy);

var rate = new LockTimeoutException(Dummy, exception);
rate.Message.Should().Be(Dummy);
rate.InnerException.Should().Be(exception);
}
}

0 comments on commit 67efc69

Please sign in to comment.