From fdcb37fb6aafde554d3ca00df5c04bfeb3fff8e0 Mon Sep 17 00:00:00 2001
From: iamdmitrij <3024338+iamdmitrij@users.noreply.github.com>
Date: Tue, 14 May 2024 17:06:34 +0300
Subject: [PATCH 1/2] Fix IDE1006
Fix public API file
Remove breaking changes from PublicAPI.Shipped.txt
Move suppressions above summary comments
Remove S3872
---
src/Polly/Caching/ContextualTtl.cs | 4 +-
src/Polly/Caching/NonSlidingTtl.cs | 2 +
src/Polly/Caching/RelativeTtl.cs | 6 +-
src/Polly/Caching/SlidingTtl.cs | 6 +-
.../AdvancedCircuitController.cs | 12 +--
.../AsyncCircuitBreakerPolicy.cs | 30 +++----
.../CircuitBreaker/CircuitBreakerPolicy.cs | 30 +++----
.../CircuitBreaker/CircuitStateController.cs | 82 +++++++++----------
.../ConsecutiveCountCircuitController.cs | 12 +--
src/Polly/Context.Dictionary.cs | 6 +-
src/Polly/IsPolicy.cs | 2 +
src/Polly/PolicyBase.ContextAndKeys.cs | 2 +
src/Polly/Polly.csproj | 13 ++-
.../LockFreeTokenBucketRateLimiter.cs | 36 ++++----
.../AdvancedCircuitBreakerAsyncSpecs.cs | 10 +--
.../AdvancedCircuitBreakerSpecs.cs | 10 +--
.../CircuitBreakerAsyncSpecs.cs | 10 +--
.../CircuitBreaker/CircuitBreakerSpecs.cs | 10 +--
.../CircuitBreakerTResultAsyncSpecs.cs | 10 +--
.../CircuitBreakerTResultSpecs.cs | 10 +--
20 files changed, 157 insertions(+), 146 deletions(-)
diff --git a/src/Polly/Caching/ContextualTtl.cs b/src/Polly/Caching/ContextualTtl.cs
index ac14b2f511f..6cc49103a45 100644
--- a/src/Polly/Caching/ContextualTtl.cs
+++ b/src/Polly/Caching/ContextualTtl.cs
@@ -17,7 +17,7 @@ public class ContextualTtl : ITtlStrategy
///
public static readonly string SlidingExpirationKey = "ContextualTtlSliding";
- private static readonly Ttl _noTtl = new(TimeSpan.Zero, false);
+ private static readonly Ttl NoTtl = new(TimeSpan.Zero, false);
///
/// Gets the TimeSpan for which to keep an item about to be cached, which may be influenced by data in the execution context.
@@ -29,7 +29,7 @@ public Ttl GetTtl(Context context, object? result)
{
if (!context.ContainsKey(TimeSpanKey))
{
- return _noTtl;
+ return NoTtl;
}
bool sliding = false;
diff --git a/src/Polly/Caching/NonSlidingTtl.cs b/src/Polly/Caching/NonSlidingTtl.cs
index 8f4cc09b9c0..bc0bb0b9866 100644
--- a/src/Polly/Caching/NonSlidingTtl.cs
+++ b/src/Polly/Caching/NonSlidingTtl.cs
@@ -6,10 +6,12 @@ namespace Polly.Caching;
///
public abstract class NonSlidingTtl : ITtlStrategy
{
+#pragma warning disable IDE1006
///
/// The absolute expiration time for cache items, represented by this strategy.
///
protected readonly DateTimeOffset absoluteExpirationTime;
+#pragma warning restore IDE1006
///
/// Initializes a new instance of the class.
diff --git a/src/Polly/Caching/RelativeTtl.cs b/src/Polly/Caching/RelativeTtl.cs
index 4f05440ca0c..90d4307aa84 100644
--- a/src/Polly/Caching/RelativeTtl.cs
+++ b/src/Polly/Caching/RelativeTtl.cs
@@ -6,7 +6,7 @@ namespace Polly.Caching;
///
public class RelativeTtl : ITtlStrategy
{
- private readonly TimeSpan ttl;
+ private readonly TimeSpan _ttl;
///
/// Initializes a new instance of the class.
@@ -19,7 +19,7 @@ public RelativeTtl(TimeSpan ttl)
throw new ArgumentOutOfRangeException(nameof(ttl), "The ttl for items to cache must be greater than zero.");
}
- this.ttl = ttl;
+ _ttl = ttl;
}
///
@@ -28,5 +28,5 @@ public RelativeTtl(TimeSpan ttl)
/// The execution context.
/// The execution result.
/// A representing the remaining Ttl of the cached item.
- public Ttl GetTtl(Context context, object? result) => new(ttl);
+ public Ttl GetTtl(Context context, object? result) => new(_ttl);
}
diff --git a/src/Polly/Caching/SlidingTtl.cs b/src/Polly/Caching/SlidingTtl.cs
index 7adfdc0b2ed..4c3c216445f 100644
--- a/src/Polly/Caching/SlidingTtl.cs
+++ b/src/Polly/Caching/SlidingTtl.cs
@@ -6,7 +6,7 @@ namespace Polly.Caching;
///
public class SlidingTtl : ITtlStrategy
{
- private readonly Ttl ttl;
+ private readonly Ttl _ttl;
///
/// Initializes a new instance of the class.
@@ -19,7 +19,7 @@ public SlidingTtl(TimeSpan slidingTtl)
throw new ArgumentOutOfRangeException(nameof(slidingTtl), "The ttl for items to cache must be greater than zero.");
}
- ttl = new Ttl(slidingTtl, true);
+ _ttl = new Ttl(slidingTtl, true);
}
///
@@ -29,5 +29,5 @@ public SlidingTtl(TimeSpan slidingTtl)
/// The execution result.
/// A representing the remaining Ttl of the cached item.
public Ttl GetTtl(Context context, object? result) =>
- ttl;
+ _ttl;
}
diff --git a/src/Polly/CircuitBreaker/AdvancedCircuitController.cs b/src/Polly/CircuitBreaker/AdvancedCircuitController.cs
index 3813c1de991..15e69154599 100644
--- a/src/Polly/CircuitBreaker/AdvancedCircuitController.cs
+++ b/src/Polly/CircuitBreaker/AdvancedCircuitController.cs
@@ -29,7 +29,7 @@ public AdvancedCircuitController(
public override void OnCircuitReset(Context context)
{
- using var _ = TimedLock.Lock(_lock);
+ using var _ = TimedLock.Lock(Lock);
// Is only null during initialization of the current class
// as the variable is not set, before the base class calls
@@ -41,9 +41,9 @@ public override void OnCircuitReset(Context context)
public override void OnActionSuccess(Context context)
{
- using var _ = TimedLock.Lock(_lock);
+ using var _ = TimedLock.Lock(Lock);
- switch (_circuitState)
+ switch (InternalCircuitState)
{
case CircuitState.HalfOpen:
OnCircuitReset(context);
@@ -65,11 +65,11 @@ public override void OnActionSuccess(Context context)
public override void OnActionFailure(DelegateResult outcome, Context context)
{
- using var _ = TimedLock.Lock(_lock);
+ using var _ = TimedLock.Lock(Lock);
- _lastOutcome = outcome;
+ LastOutcome = outcome;
- switch (_circuitState)
+ switch (InternalCircuitState)
{
case CircuitState.HalfOpen:
Break_NeedsLock(context);
diff --git a/src/Polly/CircuitBreaker/AsyncCircuitBreakerPolicy.cs b/src/Polly/CircuitBreaker/AsyncCircuitBreakerPolicy.cs
index e8d7ad4691e..cbe97e8da28 100644
--- a/src/Polly/CircuitBreaker/AsyncCircuitBreakerPolicy.cs
+++ b/src/Polly/CircuitBreaker/AsyncCircuitBreakerPolicy.cs
@@ -5,36 +5,36 @@
///
public class AsyncCircuitBreakerPolicy : AsyncPolicy, ICircuitBreakerPolicy
{
- internal readonly ICircuitController _breakerController;
+ internal readonly ICircuitController BreakerController;
internal AsyncCircuitBreakerPolicy(
PolicyBuilder policyBuilder,
ICircuitController breakerController)
: base(policyBuilder) =>
- _breakerController = breakerController;
+ BreakerController = breakerController;
///
/// Gets the state of the underlying circuit.
///
- public CircuitState CircuitState => _breakerController.CircuitState;
+ public CircuitState CircuitState => BreakerController.CircuitState;
///
/// Gets the last exception handled by the circuit-breaker.
/// This will be null if no exceptions have been handled by the circuit-breaker since the circuit last closed.
///
- public Exception LastException => _breakerController.LastException;
+ public Exception LastException => BreakerController.LastException;
///
/// Isolates (opens) the circuit manually, and holds it in this state until a call to is made.
///
public void Isolate() =>
- _breakerController.Isolate();
+ BreakerController.Isolate();
///
/// Closes the circuit, and resets any statistics controlling automated circuit-breaking.
///
public void Reset() =>
- _breakerController.Reset();
+ BreakerController.Reset();
///
protected override async Task ImplementationAsync(Func> action, Context context, CancellationToken cancellationToken,
@@ -48,7 +48,7 @@ await AsyncCircuitBreakerEngine.ImplementationAsync(
continueOnCapturedContext,
ExceptionPredicates,
ResultPredicates.None,
- _breakerController).ConfigureAwait(continueOnCapturedContext);
+ BreakerController).ConfigureAwait(continueOnCapturedContext);
return result;
}
}
@@ -59,42 +59,42 @@ await AsyncCircuitBreakerEngine.ImplementationAsync(
/// The return type of delegates which may be executed through the policy.
public class AsyncCircuitBreakerPolicy : AsyncPolicy, ICircuitBreakerPolicy
{
- internal readonly ICircuitController _breakerController;
+ internal readonly ICircuitController BreakerController;
internal AsyncCircuitBreakerPolicy(
PolicyBuilder policyBuilder,
ICircuitController breakerController)
: base(policyBuilder) =>
- _breakerController = breakerController;
+ BreakerController = breakerController;
///
/// Gets the state of the underlying circuit.
///
- public CircuitState CircuitState => _breakerController.CircuitState;
+ public CircuitState CircuitState => BreakerController.CircuitState;
///
/// Gets the last exception handled by the circuit-breaker.
/// This will be null if no exceptions have been handled by the circuit-breaker since the circuit last closed.
///
- public Exception LastException => _breakerController.LastException;
+ public Exception LastException => BreakerController.LastException;
///
/// Gets the last result returned from a user delegate which the circuit-breaker handled.
/// This will be default() if no results have been handled by the circuit-breaker since the circuit last closed, or if the last event handled by the circuit was an exception.
///
- public TResult LastHandledResult => _breakerController.LastHandledResult;
+ public TResult LastHandledResult => BreakerController.LastHandledResult;
///
/// Isolates (opens) the circuit manually, and holds it in this state until a call to is made.
///
public void Isolate() =>
- _breakerController.Isolate();
+ BreakerController.Isolate();
///
/// Closes the circuit, and resets any statistics controlling automated circuit-breaking.
///
public void Reset() =>
- _breakerController.Reset();
+ BreakerController.Reset();
///
[DebuggerStepThrough]
@@ -107,5 +107,5 @@ protected override Task ImplementationAsync(Func
public class CircuitBreakerPolicy : Policy, ICircuitBreakerPolicy
{
- internal readonly ICircuitController _breakerController;
+ internal readonly ICircuitController BreakerController;
internal CircuitBreakerPolicy(
PolicyBuilder policyBuilder,
ICircuitController breakerController)
: base(policyBuilder) =>
- _breakerController = breakerController;
+ BreakerController = breakerController;
///
/// Gets the state of the underlying circuit.
///
- public CircuitState CircuitState => _breakerController.CircuitState;
+ public CircuitState CircuitState => BreakerController.CircuitState;
///
/// Gets the last exception handled by the circuit-breaker.
/// This will be null if no exceptions have been handled by the circuit-breaker since the circuit last closed.
///
- public Exception LastException => _breakerController.LastException;
+ public Exception LastException => BreakerController.LastException;
///
/// Isolates (opens) the circuit manually, and holds it in this state until a call to is made.
///
public void Isolate() =>
- _breakerController.Isolate();
+ BreakerController.Isolate();
///
/// Closes the circuit, and resets any statistics controlling automated circuit-breaking.
///
public void Reset() =>
- _breakerController.Reset();
+ BreakerController.Reset();
///
[DebuggerStepThrough]
@@ -47,7 +47,7 @@ protected override TResult Implementation(Func.None,
- _breakerController);
+ BreakerController);
return result;
}
}
@@ -58,42 +58,42 @@ protected override TResult Implementation(FuncThe type of the result.
public class CircuitBreakerPolicy : Policy, ICircuitBreakerPolicy
{
- internal readonly ICircuitController _breakerController;
+ internal readonly ICircuitController BreakerController;
internal CircuitBreakerPolicy(
PolicyBuilder policyBuilder,
ICircuitController breakerController)
: base(policyBuilder) =>
- _breakerController = breakerController;
+ BreakerController = breakerController;
///
/// Gets the state of the underlying circuit.
///
- public CircuitState CircuitState => _breakerController.CircuitState;
+ public CircuitState CircuitState => BreakerController.CircuitState;
///
/// Gets the last exception handled by the circuit-breaker.
/// This will be null if no exceptions have been handled by the circuit-breaker since the circuit last closed, or if the last event handled by the circuit was a handled value.
///
- public Exception LastException => _breakerController.LastException;
+ public Exception LastException => BreakerController.LastException;
///
/// Gets the last result returned from a user delegate which the circuit-breaker handled.
/// This will be default() if no results have been handled by the circuit-breaker since the circuit last closed, or if the last event handled by the circuit was an exception.
///
- public TResult LastHandledResult => _breakerController.LastHandledResult;
+ public TResult LastHandledResult => BreakerController.LastHandledResult;
///
/// Isolates (opens) the circuit manually, and holds it in this state until a call to is made.
///
public void Isolate() =>
- _breakerController.Isolate();
+ BreakerController.Isolate();
///
/// Closes the circuit, and resets any statistics controlling automated circuit-breaking.
///
public void Reset() =>
- _breakerController.Reset();
+ BreakerController.Reset();
///
[DebuggerStepThrough]
@@ -104,5 +104,5 @@ protected override TResult Implementation(Func : ICircuitController
{
- protected readonly TimeSpan _durationOfBreak;
- protected readonly Action, CircuitState, TimeSpan, Context> _onBreak;
- protected readonly Action _onReset;
- protected readonly Action _onHalfOpen;
- protected readonly object _lock = new();
+ protected readonly TimeSpan DurationOfBreak;
+ protected readonly Action, CircuitState, TimeSpan, Context> OnBreak;
+ protected readonly Action OnReset;
+ protected readonly Action OnHalfOpen;
+ protected readonly object Lock = new();
- protected long _blockedTill;
- protected CircuitState _circuitState;
- protected DelegateResult _lastOutcome;
+ protected long BlockedTill;
+ protected CircuitState InternalCircuitState;
+ protected DelegateResult LastOutcome;
protected CircuitStateController(
TimeSpan durationOfBreak,
@@ -18,12 +18,12 @@ protected CircuitStateController(
Action onReset,
Action onHalfOpen)
{
- _durationOfBreak = durationOfBreak;
- _onBreak = onBreak;
- _onReset = onReset;
- _onHalfOpen = onHalfOpen;
+ DurationOfBreak = durationOfBreak;
+ OnBreak = onBreak;
+ OnReset = onReset;
+ OnHalfOpen = onHalfOpen;
- _circuitState = CircuitState.Closed;
+ InternalCircuitState = CircuitState.Closed;
Reset();
}
@@ -31,22 +31,22 @@ public CircuitState CircuitState
{
get
{
- if (_circuitState != CircuitState.Open)
+ if (InternalCircuitState != CircuitState.Open)
{
- return _circuitState;
+ return InternalCircuitState;
}
- using var _ = TimedLock.Lock(_lock);
+ using var _ = TimedLock.Lock(Lock);
#pragma warning disable CA1508 // Avoid dead conditional code. _circuitState is checked again in the lock
- if (_circuitState == CircuitState.Open && !IsInAutomatedBreak_NeedsLock)
+ if (InternalCircuitState == CircuitState.Open && !IsInAutomatedBreak_NeedsLock)
{
- _circuitState = CircuitState.HalfOpen;
- _onHalfOpen();
+ InternalCircuitState = CircuitState.HalfOpen;
+ OnHalfOpen();
}
#pragma warning restore CA1508 // Avoid dead conditional code. _circuitState is checked again in the lock
- return _circuitState;
+ return InternalCircuitState;
}
}
@@ -54,8 +54,8 @@ public Exception LastException
{
get
{
- using var _ = TimedLock.Lock(_lock);
- return _lastOutcome?.Exception;
+ using var _ = TimedLock.Lock(Lock);
+ return LastOutcome?.Exception;
}
}
@@ -63,35 +63,35 @@ public TResult LastHandledResult
{
get
{
- using var _ = TimedLock.Lock(_lock);
- return _lastOutcome != null ? _lastOutcome.Result : default;
+ using var _ = TimedLock.Lock(Lock);
+ return LastOutcome != null ? LastOutcome.Result : default;
}
}
- protected bool IsInAutomatedBreak_NeedsLock => SystemClock.UtcNow().Ticks < _blockedTill;
+ protected bool IsInAutomatedBreak_NeedsLock => SystemClock.UtcNow().Ticks < BlockedTill;
public void Isolate()
{
- using var _ = TimedLock.Lock(_lock);
- _lastOutcome = new DelegateResult(new IsolatedCircuitException("The circuit is manually held open and is not allowing calls."));
+ using var _ = TimedLock.Lock(Lock);
+ LastOutcome = new DelegateResult(new IsolatedCircuitException("The circuit is manually held open and is not allowing calls."));
BreakFor_NeedsLock(TimeSpan.MaxValue, Context.None());
- _circuitState = CircuitState.Isolated;
+ InternalCircuitState = CircuitState.Isolated;
}
protected void Break_NeedsLock(Context context) =>
- BreakFor_NeedsLock(_durationOfBreak, context);
+ BreakFor_NeedsLock(DurationOfBreak, context);
private void BreakFor_NeedsLock(TimeSpan durationOfBreak, Context context)
{
bool willDurationTakeUsPastDateTimeMaxValue = durationOfBreak > DateTime.MaxValue - SystemClock.UtcNow();
- _blockedTill = willDurationTakeUsPastDateTimeMaxValue
+ BlockedTill = willDurationTakeUsPastDateTimeMaxValue
? DateTime.MaxValue.Ticks
: (SystemClock.UtcNow() + durationOfBreak).Ticks;
- var transitionedState = _circuitState;
- _circuitState = CircuitState.Open;
+ var transitionedState = InternalCircuitState;
+ InternalCircuitState = CircuitState.Open;
- _onBreak(_lastOutcome, transitionedState, durationOfBreak, context);
+ OnBreak(LastOutcome, transitionedState, durationOfBreak, context);
}
public void Reset() =>
@@ -99,20 +99,20 @@ public void Reset() =>
protected void ResetInternal_NeedsLock(Context context)
{
- _blockedTill = DateTime.MinValue.Ticks;
- _lastOutcome = null;
+ BlockedTill = DateTime.MinValue.Ticks;
+ LastOutcome = null;
- CircuitState priorState = _circuitState;
- _circuitState = CircuitState.Closed;
+ CircuitState priorState = InternalCircuitState;
+ InternalCircuitState = CircuitState.Closed;
if (priorState != CircuitState.Closed)
{
- _onReset(context);
+ OnReset(context);
}
}
protected bool PermitHalfOpenCircuitTest()
{
- long currentlyBlockedUntil = _blockedTill;
+ long currentlyBlockedUntil = BlockedTill;
if (SystemClock.UtcNow().Ticks < currentlyBlockedUntil)
{
return false;
@@ -120,14 +120,14 @@ protected bool PermitHalfOpenCircuitTest()
// It's time to permit a / another trial call in the half-open state ...
// ... but to prevent race conditions/multiple calls, we have to ensure only _one_ thread wins the race to own this next call.
- return Interlocked.CompareExchange(ref _blockedTill, SystemClock.UtcNow().Ticks + _durationOfBreak.Ticks, currentlyBlockedUntil) == currentlyBlockedUntil;
+ return Interlocked.CompareExchange(ref BlockedTill, SystemClock.UtcNow().Ticks + DurationOfBreak.Ticks, currentlyBlockedUntil) == currentlyBlockedUntil;
}
private BrokenCircuitException GetBreakingException()
{
const string BrokenCircuitMessage = "The circuit is now open and is not allowing calls.";
- var lastOutcome = _lastOutcome;
+ var lastOutcome = LastOutcome;
if (lastOutcome == null)
{
return new BrokenCircuitException(BrokenCircuitMessage);
diff --git a/src/Polly/CircuitBreaker/ConsecutiveCountCircuitController.cs b/src/Polly/CircuitBreaker/ConsecutiveCountCircuitController.cs
index 2dc1cafb3bb..a498c208f91 100644
--- a/src/Polly/CircuitBreaker/ConsecutiveCountCircuitController.cs
+++ b/src/Polly/CircuitBreaker/ConsecutiveCountCircuitController.cs
@@ -16,16 +16,16 @@ public ConsecutiveCountCircuitController(
public override void OnCircuitReset(Context context)
{
- using var _ = TimedLock.Lock(_lock);
+ using var _ = TimedLock.Lock(Lock);
_consecutiveFailureCount = 0;
ResetInternal_NeedsLock(context);
}
public override void OnActionSuccess(Context context)
{
- using var _ = TimedLock.Lock(_lock);
+ using var _ = TimedLock.Lock(Lock);
- switch (_circuitState)
+ switch (InternalCircuitState)
{
case CircuitState.HalfOpen:
OnCircuitReset(context);
@@ -46,11 +46,11 @@ public override void OnActionSuccess(Context context)
public override void OnActionFailure(DelegateResult outcome, Context context)
{
- using var _ = TimedLock.Lock(_lock);
+ using var _ = TimedLock.Lock(Lock);
- _lastOutcome = outcome;
+ LastOutcome = outcome;
- switch (_circuitState)
+ switch (InternalCircuitState)
{
case CircuitState.HalfOpen:
Break_NeedsLock(context);
diff --git a/src/Polly/Context.Dictionary.cs b/src/Polly/Context.Dictionary.cs
index bbff2d05ed1..079f7707e89 100644
--- a/src/Polly/Context.Dictionary.cs
+++ b/src/Polly/Context.Dictionary.cs
@@ -9,9 +9,9 @@ public partial class Context : IDictionary, IDictionary, IReadOn
// For an individual execution through a policy or policywrap, it is expected that all execution steps (for example executing the user delegate, invoking policy-activity delegates such as onRetry, onBreak, onTimeout etc) execute sequentially.
// Therefore, this class is intentionally not constructed to be safe for concurrent access from multiple threads.
- private Dictionary wrappedDictionary;
+ private Dictionary _wrappedDictionary;
- private Dictionary WrappedDictionary => wrappedDictionary ?? (wrappedDictionary = []);
+ private Dictionary WrappedDictionary => _wrappedDictionary ?? (_wrappedDictionary = []);
///
/// Initializes a new instance of the class, with the specified and the supplied .
@@ -30,7 +30,7 @@ internal Context(IDictionary contextData)
throw new ArgumentNullException(nameof(contextData));
}
- wrappedDictionary = new Dictionary(contextData);
+ _wrappedDictionary = new Dictionary(contextData);
}
#region IDictionary implementation
diff --git a/src/Polly/IsPolicy.cs b/src/Polly/IsPolicy.cs
index fcdbf7d46ee..0548687d4be 100644
--- a/src/Polly/IsPolicy.cs
+++ b/src/Polly/IsPolicy.cs
@@ -3,7 +3,9 @@
///
/// A marker interface identifying Polly policies of all types, and containing properties common to all policies.
///
+#pragma warning disable IDE1006
public interface IsPolicy
+#pragma warning restore IDE1006
{
///
/// Gets a key intended to be unique to each policy instance, which is passed with executions as the property.
diff --git a/src/Polly/PolicyBase.ContextAndKeys.cs b/src/Polly/PolicyBase.ContextAndKeys.cs
index 6a3f6325b03..6db9e16c0db 100644
--- a/src/Polly/PolicyBase.ContextAndKeys.cs
+++ b/src/Polly/PolicyBase.ContextAndKeys.cs
@@ -2,10 +2,12 @@
public abstract partial class PolicyBase
{
+#pragma warning disable IDE1006
///
/// A key intended to be unique to each instance.
///
protected string policyKeyInternal;
+#pragma warning restore IDE1006
///
/// Gets a key intended to be unique to each instance, which is passed with executions as the property.
diff --git a/src/Polly/Polly.csproj b/src/Polly/Polly.csproj
index 651825f1047..52be1f818da 100644
--- a/src/Polly/Polly.csproj
+++ b/src/Polly/Polly.csproj
@@ -8,19 +8,24 @@
70
true
$(NoWarn);SA1414;S3215
- $(NoWarn);IDE1006;CA1062;CA1068;S4039;CA1063;CA1031;CA1051
+ $(NoWarn);CA1062;CA1068;S4039;CA1063;CA1031;CA1051
$(NoWarn);CA2211;S2223;CA1032;CA1815;CA1816;S4457;CA1033
$(NoWarn);CA1010;CA1064;SA1118
$(NoWarn);S3971;CA1724;CA1716;SA1108;CA1710;S4049;S3246
$(NoWarn);CA1805
-
+
$(NoWarn);RS0037;
- Polly is a .NET resilience and transient-fault-handling library that allows developers to express resilience and transient fault handling policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner.
- Polly Exception Handling Resilience Transient Fault Policy Circuit Breaker CircuitBreaker Retry Wait Cache Cache-aside Bulkhead Fallback Timeout Throttle
+ Polly is a .NET resilience and transient-fault-handling library that allows
+ developers to express resilience and transient fault handling policies such as Retry, Circuit
+ Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner.
+ Polly Exception Handling Resilience Transient Fault Policy Circuit Breaker
+ CircuitBreaker Retry Wait Cache Cache-aside Bulkhead Fallback Timeout Throttle
diff --git a/src/Polly/RateLimit/LockFreeTokenBucketRateLimiter.cs b/src/Polly/RateLimit/LockFreeTokenBucketRateLimiter.cs
index bb2ab0829e9..dce3814a7b6 100644
--- a/src/Polly/RateLimit/LockFreeTokenBucketRateLimiter.cs
+++ b/src/Polly/RateLimit/LockFreeTokenBucketRateLimiter.cs
@@ -7,15 +7,15 @@ namespace Polly.RateLimit;
///
internal sealed class LockFreeTokenBucketRateLimiter : IRateLimiter
{
- private readonly long addTokenTickInterval;
- private readonly long bucketCapacity;
+ private readonly long _addTokenTickInterval;
+ private readonly long _bucketCapacity;
- private long currentTokens;
+ private long _currentTokens;
- private long addNextTokenAtTicks;
+ private long _addNextTokenAtTicks;
#if !NETSTANDARD2_0
- private SpinWait spinner = default;
+ private SpinWait _spinner = default;
#endif
///
@@ -37,11 +37,11 @@ public LockFreeTokenBucketRateLimiter(TimeSpan onePer, long bucketCapacity)
throw new ArgumentOutOfRangeException(nameof(bucketCapacity), bucketCapacity, $"{nameof(bucketCapacity)} must be greater than or equal to 1.");
}
- addTokenTickInterval = onePer.Ticks;
- this.bucketCapacity = bucketCapacity;
+ _addTokenTickInterval = onePer.Ticks;
+ _bucketCapacity = bucketCapacity;
- currentTokens = bucketCapacity;
- addNextTokenAtTicks = SystemClock.DateTimeOffsetUtcNow().Ticks + addTokenTickInterval;
+ _currentTokens = bucketCapacity;
+ _addNextTokenAtTicks = SystemClock.DateTimeOffsetUtcNow().Ticks + _addTokenTickInterval;
}
///
@@ -52,7 +52,7 @@ public LockFreeTokenBucketRateLimiter(TimeSpan onePer, long bucketCapacity)
while (true)
{
// Try to get a token.
- long tokensAfterGrabOne = Interlocked.Decrement(ref currentTokens);
+ long tokensAfterGrabOne = Interlocked.Decrement(ref _currentTokens);
if (tokensAfterGrabOne >= 0)
{
@@ -62,7 +62,7 @@ public LockFreeTokenBucketRateLimiter(TimeSpan onePer, long bucketCapacity)
// No tokens! We're rate-limited - unless we can refill the bucket.
long now = SystemClock.DateTimeOffsetUtcNow().Ticks;
- long currentAddNextTokenAtTicks = Interlocked.Read(ref addNextTokenAtTicks);
+ long currentAddNextTokenAtTicks = Interlocked.Read(ref _addNextTokenAtTicks);
long ticksTillAddNextToken = currentAddNextTokenAtTicks - now;
if (ticksTillAddNextToken > 0)
@@ -75,19 +75,19 @@ public LockFreeTokenBucketRateLimiter(TimeSpan onePer, long bucketCapacity)
// We definitely need to add one token. In fact, if we haven't hit this bit of code for a while, we might be due to add a bunch of tokens.
// Passing addNextTokenAtTicks merits one token and any whole token tick intervals further each merit another.
- long tokensMissedAdding = 1 + (-ticksTillAddNextToken / addTokenTickInterval);
+ long tokensMissedAdding = 1 + (-ticksTillAddNextToken / _addTokenTickInterval);
// We mustn't exceed bucket capacity though.
- long tokensToAdd = Math.Min(bucketCapacity, tokensMissedAdding);
+ long tokensToAdd = Math.Min(_bucketCapacity, tokensMissedAdding);
// Work out when tokens would next be due to be added, if we add these tokens.
- long newAddNextTokenAtTicks = currentAddNextTokenAtTicks + (tokensToAdd * addTokenTickInterval);
+ long newAddNextTokenAtTicks = currentAddNextTokenAtTicks + (tokensToAdd * _addTokenTickInterval);
// But if we were way overdue refilling the bucket (there was inactivity for a while), that value would be out-of-date: the next time we add tokens must be at least addTokenTickInterval from now.
- newAddNextTokenAtTicks = Math.Max(newAddNextTokenAtTicks, now + addTokenTickInterval);
+ newAddNextTokenAtTicks = Math.Max(newAddNextTokenAtTicks, now + _addTokenTickInterval);
// Now see if we win the race to add these tokens. Other threads might be racing through this code at the same time: only one thread must add the tokens!
- if (Interlocked.CompareExchange(ref addNextTokenAtTicks, newAddNextTokenAtTicks, currentAddNextTokenAtTicks) == currentAddNextTokenAtTicks)
+ if (Interlocked.CompareExchange(ref _addNextTokenAtTicks, newAddNextTokenAtTicks, currentAddNextTokenAtTicks) == currentAddNextTokenAtTicks)
{
// We won the race to add the tokens!
@@ -99,7 +99,7 @@ public LockFreeTokenBucketRateLimiter(TimeSpan onePer, long bucketCapacity)
// The advantage of only adding tokens when the bucket is empty is that we can now hard set the new amount of tokens (Interlocked.Exchange) without caring if other threads have simultaneously been taking or adding tokens.
// (If we added a token per addTokenTickInterval to a non-empty bucket, the reasoning about not overflowing the bucket seems harder.)
- Interlocked.Exchange(ref currentTokens, tokensToAdd - 1);
+ Interlocked.Exchange(ref _currentTokens, tokensToAdd - 1);
return (true, TimeSpan.Zero);
}
else
@@ -110,7 +110,7 @@ public LockFreeTokenBucketRateLimiter(TimeSpan onePer, long bucketCapacity)
#if NETSTANDARD2_0
Thread.Sleep(0);
#else
- spinner.SpinOnce();
+ _spinner.SpinOnce();
#endif
}
}
diff --git a/test/Polly.Specs/CircuitBreaker/AdvancedCircuitBreakerAsyncSpecs.cs b/test/Polly.Specs/CircuitBreaker/AdvancedCircuitBreakerAsyncSpecs.cs
index eabd3a0b2b3..fc930f8be4a 100644
--- a/test/Polly.Specs/CircuitBreaker/AdvancedCircuitBreakerAsyncSpecs.cs
+++ b/test/Polly.Specs/CircuitBreaker/AdvancedCircuitBreakerAsyncSpecs.cs
@@ -1459,11 +1459,11 @@ await breaker.Awaiting(x => x.RaiseExceptionAsync())
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should permit first execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should reject a second execution. (tho still in half-open condition).
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().Throw();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().Throw();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
}
@@ -1495,11 +1495,11 @@ await breaker.Awaiting(x => x.RaiseExceptionAsync())
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should permit first execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should reject a second execution. (tho still in half-open condition).
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().Throw();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().Throw();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// Allow another time window to pass (breaker should still be HalfOpen).
@@ -1507,7 +1507,7 @@ await breaker.Awaiting(x => x.RaiseExceptionAsync())
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should now permit another trial execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
}
diff --git a/test/Polly.Specs/CircuitBreaker/AdvancedCircuitBreakerSpecs.cs b/test/Polly.Specs/CircuitBreaker/AdvancedCircuitBreakerSpecs.cs
index c95950a622e..bd233a5905b 100644
--- a/test/Polly.Specs/CircuitBreaker/AdvancedCircuitBreakerSpecs.cs
+++ b/test/Polly.Specs/CircuitBreaker/AdvancedCircuitBreakerSpecs.cs
@@ -1458,11 +1458,11 @@ public void Should_only_allow_single_execution_on_first_entering_halfopen_state_
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should permit first execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should reject a second execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().Throw();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().Throw();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
}
@@ -1495,11 +1495,11 @@ public void Should_allow_single_execution_per_break_duration_in_halfopen_state__
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should permit first execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should reject a second execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().Throw();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().Throw();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// Allow another time window to pass (breaker should still be HalfOpen).
@@ -1507,7 +1507,7 @@ public void Should_allow_single_execution_per_break_duration_in_halfopen_state__
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should now permit another trial execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
}
diff --git a/test/Polly.Specs/CircuitBreaker/CircuitBreakerAsyncSpecs.cs b/test/Polly.Specs/CircuitBreaker/CircuitBreakerAsyncSpecs.cs
index 6dfa6b33378..71c95914a84 100644
--- a/test/Polly.Specs/CircuitBreaker/CircuitBreakerAsyncSpecs.cs
+++ b/test/Polly.Specs/CircuitBreaker/CircuitBreakerAsyncSpecs.cs
@@ -323,11 +323,11 @@ await breaker.Awaiting(x => x.RaiseExceptionAsync())
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should permit first execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should reject a second execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().Throw();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().Throw();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
}
@@ -353,11 +353,11 @@ await breaker.Awaiting(x => x.RaiseExceptionAsync())
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should permit first execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should reject a second execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().Throw();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().Throw();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// Allow another time window to pass (breaker should still be HalfOpen).
@@ -365,7 +365,7 @@ await breaker.Awaiting(x => x.RaiseExceptionAsync())
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should now permit another trial execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
}
diff --git a/test/Polly.Specs/CircuitBreaker/CircuitBreakerSpecs.cs b/test/Polly.Specs/CircuitBreaker/CircuitBreakerSpecs.cs
index d4e49262c65..14c4793a444 100644
--- a/test/Polly.Specs/CircuitBreaker/CircuitBreakerSpecs.cs
+++ b/test/Polly.Specs/CircuitBreaker/CircuitBreakerSpecs.cs
@@ -321,11 +321,11 @@ public void Should_only_allow_single_execution_on_first_entering_halfopen_state_
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should permit first execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should reject a second execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().Throw();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().Throw();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
}
@@ -351,11 +351,11 @@ public void Should_allow_single_execution_per_break_duration_in_halfopen_state__
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should permit first execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should reject a second execution in the same time window.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().Throw();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().Throw();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// Allow another time window to pass (breaker should still be HalfOpen).
@@ -363,7 +363,7 @@ public void Should_allow_single_execution_per_break_duration_in_halfopen_state__
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should now permit another trial execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
}
diff --git a/test/Polly.Specs/CircuitBreaker/CircuitBreakerTResultAsyncSpecs.cs b/test/Polly.Specs/CircuitBreaker/CircuitBreakerTResultAsyncSpecs.cs
index 5534a81f4be..5f02bfc3677 100644
--- a/test/Polly.Specs/CircuitBreaker/CircuitBreakerTResultAsyncSpecs.cs
+++ b/test/Polly.Specs/CircuitBreaker/CircuitBreakerTResultAsyncSpecs.cs
@@ -385,11 +385,11 @@ public async Task Should_only_allow_single_execution_on_first_entering_halfopen_
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should permit first execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should reject a second execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().Throw();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().Throw();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
}
@@ -415,11 +415,11 @@ public async Task Should_allow_single_execution_per_break_duration_in_halfopen_s
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should permit first execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should reject a second execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().Throw();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().Throw();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// Allow another time window to pass (breaker should still be HalfOpen).
@@ -427,7 +427,7 @@ public async Task Should_allow_single_execution_per_break_duration_in_halfopen_s
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should now permit another trial execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
}
diff --git a/test/Polly.Specs/CircuitBreaker/CircuitBreakerTResultSpecs.cs b/test/Polly.Specs/CircuitBreaker/CircuitBreakerTResultSpecs.cs
index 39be98231f0..6a991263ca3 100644
--- a/test/Polly.Specs/CircuitBreaker/CircuitBreakerTResultSpecs.cs
+++ b/test/Polly.Specs/CircuitBreaker/CircuitBreakerTResultSpecs.cs
@@ -385,11 +385,11 @@ public void Should_only_allow_single_execution_on_first_entering_halfopen_state_
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should permit first execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should reject a second execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().Throw();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().Throw();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
}
@@ -415,11 +415,11 @@ public void Should_allow_single_execution_per_break_duration_in_halfopen_state__
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should permit first execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should reject a second execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().Throw();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().Throw();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// Allow another time window to pass (breaker should still be HalfOpen).
@@ -427,7 +427,7 @@ public void Should_allow_single_execution_per_break_duration_in_halfopen_state__
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
// OnActionPreExecute() should now permit another trial execution.
- breaker._breakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
+ breaker.BreakerController.Invoking(c => c.OnActionPreExecute()).Should().NotThrow();
breaker.CircuitState.Should().Be(CircuitState.HalfOpen);
}
From 2f0d7eec6b9974e94e6322cf71b3a57a9c6af776 Mon Sep 17 00:00:00 2001
From: Dmitrij <3024338+iamdmitrij@users.noreply.github.com>
Date: Thu, 16 May 2024 08:28:39 +0300
Subject: [PATCH 2/2] Format file
---
src/Polly/Polly.csproj | 11 +++--------
1 file changed, 3 insertions(+), 8 deletions(-)
diff --git a/src/Polly/Polly.csproj b/src/Polly/Polly.csproj
index 52be1f818da..cc81cd814fe 100644
--- a/src/Polly/Polly.csproj
+++ b/src/Polly/Polly.csproj
@@ -14,18 +14,13 @@
$(NoWarn);S3971;CA1724;CA1716;SA1108;CA1710;S4049;S3246
$(NoWarn);CA1805
-
+
$(NoWarn);RS0037;
- Polly is a .NET resilience and transient-fault-handling library that allows
- developers to express resilience and transient fault handling policies such as Retry, Circuit
- Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner.
- Polly Exception Handling Resilience Transient Fault Policy Circuit Breaker
- CircuitBreaker Retry Wait Cache Cache-aside Bulkhead Fallback Timeout Throttle
+ Polly is a .NET resilience and transient-fault-handling library that allows developers to express resilience and transient fault handling policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner.
+ Polly Exception Handling Resilience Transient Fault Policy Circuit Breaker CircuitBreaker Retry Wait Cache Cache-aside Bulkhead Fallback Timeout Throttle