From 56ffa14d58079f6ab95bc8dcee8fc401f00b3927 Mon Sep 17 00:00:00 2001 From: peter-csala <57183693+peter-csala@users.noreply.github.com> Date: Sat, 28 Oct 2023 20:12:46 +0200 Subject: [PATCH] [Docs] Make quick start samples consistent (#1735) --- README.md | 324 +++++++++++++++------------- docs/getting-started.md | 6 +- docs/strategies/circuit-breaker.md | 44 ++-- docs/strategies/fallback.md | 72 +++---- docs/strategies/hedging.md | 54 ++--- docs/strategies/rate-limiter.md | 11 +- docs/strategies/retry.md | 38 ++-- docs/strategies/timeout.md | 55 +++-- src/Polly.RateLimiting/README.md | 11 +- src/Snippets/Docs/CircuitBreaker.cs | 44 ++-- src/Snippets/Docs/Fallback.cs | 72 +++---- src/Snippets/Docs/Hedging.cs | 54 ++--- src/Snippets/Docs/RateLimiter.cs | 11 +- src/Snippets/Docs/Readme.cs | 6 +- src/Snippets/Docs/Retry.cs | 38 ++-- src/Snippets/Docs/Timeout.cs | 55 +++-- 16 files changed, 468 insertions(+), 427 deletions(-) diff --git a/README.md b/README.md index 5eac80019b9..eb604743549 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ ResiliencePipeline pipeline = new ResiliencePipelineBuilder() .Build(); // Builds the resilience pipeline // Execute the pipeline asynchronously -await pipeline.ExecuteAsync(static async cancellationToken => { /*Your custom logic here */ }, cancellationToken); +await pipeline.ExecuteAsync(static async token => { /*Your custom logic goes here */ }, cancellationToken); ``` @@ -76,7 +76,7 @@ services.AddResiliencePipeline("my-pipeline", builder => }); // Build the service provider -IServiceProvider serviceProvider = services.BuildServiceProvider(); +var serviceProvider = services.BuildServiceProvider(); // Retrieve ResiliencePipelineProvider that caches and dynamically creates the resilience pipelines var pipelineProvider = serviceProvider.GetRequiredService>(); @@ -87,7 +87,7 @@ ResiliencePipeline pipeline = pipelineProvider.GetPipeline("my-pipeline"); // Execute the pipeline await pipeline.ExecuteAsync(static async token => { - // Your custom logic here + // Your custom logic goes here }); ``` @@ -98,17 +98,25 @@ Polly provides a variety of resilience strategies. Alongside the comprehensive g Polly categorizes resilience strategies into two main groups: -- **Reactive**: These strategies handle specific exceptions that are thrown, or results that are returned, by the callbacks executed through the strategy. -- **Proactive**: Unlike reactive strategies, proactive strategies do not focus on handling errors by the callbacks might throw or return. They can make pro-active decisions to cancel or reject the execution of callbacks (e.g., using a rate limiter or a timeout resilience strategy). +### Reactive -| Strategy | Reactive | Premise | AKA | How does the strategy mitigate?| -| ------------- | --- | ------------- |:-------------: |------------- | -|**Retry**
(strategy family)
([quick-start](#retry) ; [deep](https://www.pollydocs.org/strategies/retry)) |Yes|Many faults are transient and may self-correct after a short delay.| *Maybe it's just a blip* | Allows configuring automatic retries. | -|**Circuit-breaker**
(strategy family)
([quick-start](#circuit-breaker) ; [deep](https://www.pollydocs.org/strategies/circuit-breaker))|Yes|When a system is seriously struggling, failing fast is better than making users/callers wait.

Protecting a faulting system from overload can help it recover. | *Stop doing it if it hurts*

*Give that system a break* | Breaks the circuit (blocks executions) for a period, when faults exceed some pre-configured threshold. | -|**Timeout**
([quick-start](#timeout) ; [deep](https://www.pollydocs.org/strategies/timeout))|No|Beyond a certain wait, a success result is unlikely.| *Don't wait forever* |Guarantees the caller won't have to wait beyond the timeout. | -|**Rate Limiter**
([quick-start](#rate-limiter) ; [deep](https://www.pollydocs.org/strategies/rate-limiter))|No|Limiting the rate a system handles requests is another way to control load.

This can apply to the way your system accepts incoming calls, and/or to the way you call downstream services. | *Slow down a bit, will you?* |Constrains executions to not exceed a certain rate. | -|**Fallback**
([quick-start](#fallback) ; [deep](https://www.pollydocs.org/strategies/fallback))|Yes|Things will still fail - plan what you will do when that happens.| *Degrade gracefully* |Defines an alternative value to be returned (or action to be executed) on failure. | -|**Hedging**
([quick-start](#hedging) ; [deep](https://www.pollydocs.org/strategies/hedging))|Yes|Things can be slow sometimes, plan what you will do when that happens.| *Hedge your bets* | Executes parallel actions when things are slow and waits for the fastest one. | +These strategies handle specific exceptions that are thrown, or results that are returned, by the callbacks executed through the strategy. + +| Strategy | Premise | AKA | Mitigation | +| ------------- | ------------- | -------------- | ------------ | +|[**Retry** family](#retry) |Many faults are transient and may self-correct after a short delay.| *Maybe it's just a blip* | Allows configuring automatic retries. | +|[**Circuit-breaker** family](#circuit-breaker)|When a system is seriously struggling, failing fast is better than making users/callers wait.

Protecting a faulting system from overload can help it recover. | *Stop doing it if it hurts*

*Give that system a break* | Breaks the circuit (blocks executions) for a period, when faults exceed some pre-configured threshold. | +|[**Fallback**](#fallback)|Things will still fail - plan what you will do when that happens.| *Degrade gracefully* |Defines an alternative value to be returned (or action to be executed) on failure. | +|[**Hedging**](#hedging)|Things can be slow sometimes, plan what you will do when that happens.| *Hedge your bets* | Executes parallel actions when things are slow and waits for the fastest one. | + +### Proactive + +Unlike reactive strategies, proactive strategies do not focus on handling errors by the callbacks might throw or return. They can make pro-active decisions to cancel or reject the execution of callbacks. + +| Strategy | Premise | AKA | Prevention | +| ----------- | ------------- | -------------- | ------------ | +|[**Timeout**](#timeout)|Beyond a certain wait, a success result is unlikely.| *Don't wait forever* |Guarantees the caller won't have to wait beyond the timeout. | +|[**Rate Limiter**](#rate-limiter)|Limiting the rate a system handles requests is another way to control load.

This can apply to the way your system accepts incoming calls, and/or to the way you call downstream services. | *Slow down a bit, will you?* |Constrains executions to not exceed a certain rate. | Visit [resilience strategies](https://www.pollydocs.org/strategies) docs to explore how to configure individual resilience strategies in more detail. @@ -116,32 +124,32 @@ Visit [resilience strategies](https://www.pollydocs.org/strategies) docs to expl ```cs -// Add retry using the default options. +// Retry using the default options. // See https://www.pollydocs.org/strategies/retry#defaults for defaults. -new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions()); +var optionsDefaults = new RetryStrategyOptions(); // For instant retries with no delay -new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions +var optionsNoDelay = new RetryStrategyOptions { Delay = TimeSpan.Zero -}); +}; // For advanced control over the retry behavior, including the number of attempts, // delay between retries, and the types of exceptions to handle. -new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions +var optionsComplex = new RetryStrategyOptions { ShouldHandle = new PredicateBuilder().Handle(), BackoffType = DelayBackoffType.Exponential, UseJitter = true, // Adds a random factor to the delay MaxRetryAttempts = 4, Delay = TimeSpan.FromSeconds(3), -}); +}; // To use a custom function to generate the delay for retries -new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions +var optionsDelayGenerator = new RetryStrategyOptions { MaxRetryAttempts = 2, - DelayGenerator = args => + DelayGenerator = static args => { var delay = args.AttemptNumber switch { @@ -154,12 +162,12 @@ new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions // but the API also supports asynchronous implementations. return new ValueTask(delay); } -}); +}; // To extract the delay from the result object -new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions +var optionsExtractDelay = new RetryStrategyOptions { - DelayGenerator = args => + DelayGenerator = static args => { if (args.Outcome.Result is HttpResponseMessage responseMessage && TryGetDelay(responseMessage, out TimeSpan delay)) @@ -170,67 +178,73 @@ new ResiliencePipelineBuilder().AddRetry(new RetryStrategyO // Returning null means the retry strategy will use its internal delay for this attempt. return new ValueTask((TimeSpan?)null); } -}); +}; // To get notifications when a retry is performed -new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions +var optionsOnRetry = new RetryStrategyOptions { MaxRetryAttempts = 2, - OnRetry = args => + OnRetry = static args => { Console.WriteLine("OnRetry, Attempt: {0}", args.AttemptNumber); // Event handlers can be asynchronous; here, we return an empty ValueTask. return default; } -}); +}; // To keep retrying indefinitely or until success use int.MaxValue. -new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions +var optionsIndefiniteRetry = new RetryStrategyOptions { MaxRetryAttempts = int.MaxValue, -}); +}; + +// Add a retry strategy with a RetryStrategyOptions{} instance to the pipeline +new ResiliencePipelineBuilder().AddRetry(optionsDefaults); +new ResiliencePipelineBuilder().AddRetry(optionsExtractDelay); ``` -If all retries fail, a retry strategy rethrows the final exception back to the calling code. For more details, visit the [retry strategy](https://www.pollydocs.org/strategies/retry) documentation. +If all retries fail, a retry strategy rethrows the final exception back to the calling code. + +For more details, visit the [retry strategy](https://www.pollydocs.org/strategies/retry) documentation. ### Circuit Breaker ```cs -// Add circuit breaker with default options. +// Circuit breaker with default options. // See https://www.pollydocs.org/strategies/circuit-breaker#defaults for defaults. -new ResiliencePipelineBuilder().AddCircuitBreaker(new CircuitBreakerStrategyOptions()); +var optionsDefaults = new CircuitBreakerStrategyOptions(); -// Add circuit breaker with customized options: -// +// Circuit breaker with customized options: // The circuit will break if more than 50% of actions result in handled exceptions, // within any 10-second sampling duration, and at least 8 actions are processed. -new ResiliencePipelineBuilder().AddCircuitBreaker(new CircuitBreakerStrategyOptions +var optionsComplex = new CircuitBreakerStrategyOptions { FailureRatio = 0.5, SamplingDuration = TimeSpan.FromSeconds(10), MinimumThroughput = 8, BreakDuration = TimeSpan.FromSeconds(30), ShouldHandle = new PredicateBuilder().Handle() -}); +}; // Handle specific failed results for HttpResponseMessage: -new ResiliencePipelineBuilder() - .AddCircuitBreaker(new CircuitBreakerStrategyOptions - { - ShouldHandle = new PredicateBuilder() - .Handle() - .HandleResult(response => response.StatusCode == HttpStatusCode.InternalServerError) - }); +var optionsShouldHandle = new CircuitBreakerStrategyOptions +{ + ShouldHandle = new PredicateBuilder() + .Handle() + .HandleResult(response => response.StatusCode == HttpStatusCode.InternalServerError) +}; // Monitor the circuit state, useful for health reporting: var stateProvider = new CircuitBreakerStateProvider(); +var optionsStateProvider = new CircuitBreakerStrategyOptions +{ + StateProvider = stateProvider +}; -new ResiliencePipelineBuilder() - .AddCircuitBreaker(new() { StateProvider = stateProvider }) - .Build(); +var circuitState = stateProvider.CircuitState; /* CircuitState.Closed - Normal operation; actions are executed. @@ -241,16 +255,20 @@ CircuitState.Isolated - Circuit is manually held open; actions are blocked. // Manually control the Circuit Breaker state: var manualControl = new CircuitBreakerManualControl(); - -new ResiliencePipelineBuilder() - .AddCircuitBreaker(new() { ManualControl = manualControl }) - .Build(); +var optionsManualControl = new CircuitBreakerStrategyOptions +{ + ManualControl = manualControl +}; // Manually isolate a circuit, e.g., to isolate a downstream service. await manualControl.IsolateAsync(); // Manually close the circuit to allow actions to be executed again. await manualControl.CloseAsync(); + +// Add a circuit breaker strategy with a CircuitBreakerStrategyOptions{} instance to the pipeline +new ResiliencePipelineBuilder().AddCircuitBreaker(optionsDefaults); +new ResiliencePipelineBuilder().AddCircuitBreaker(optionsStateProvider); ``` @@ -260,48 +278,48 @@ For more details, visit the [circuit breaker strategy](https://www.pollydocs.org ```cs -// Add a fallback/substitute value if an operation fails. -new ResiliencePipelineBuilder() - .AddFallback(new FallbackStrategyOptions - { - ShouldHandle = new PredicateBuilder() - .Handle() - .HandleResult(r => r is null), - FallbackAction = args => Outcome.FromResultAsValueTask(UserAvatar.Blank) - }); +// A fallback/substitute value if an operation fails. +var optionsSubstitute = new FallbackStrategyOptions +{ + ShouldHandle = new PredicateBuilder() + .Handle() + .HandleResult(r => r is null), + FallbackAction = static args => Outcome.FromResultAsValueTask(UserAvatar.Blank) +}; // Use a dynamically generated value if an operation fails. -new ResiliencePipelineBuilder() - .AddFallback(new FallbackStrategyOptions +var optionsFallbackAction = new FallbackStrategyOptions +{ + ShouldHandle = new PredicateBuilder() + .Handle() + .HandleResult(r => r is null), + FallbackAction = static args => { - ShouldHandle = new PredicateBuilder() - .Handle() - .HandleResult(r => r is null), - FallbackAction = args => - { - var avatar = UserAvatar.GetRandomAvatar(); - return Outcome.FromResultAsValueTask(avatar); - } - }); + var avatar = UserAvatar.GetRandomAvatar(); + return Outcome.FromResultAsValueTask(avatar); + } +}; // Use a default or dynamically generated value, and execute an additional action if the fallback is triggered. -new ResiliencePipelineBuilder() - .AddFallback(new FallbackStrategyOptions +var optionsOnFallback = new FallbackStrategyOptions +{ + ShouldHandle = new PredicateBuilder() + .Handle() + .HandleResult(r => r is null), + FallbackAction = static args => { - ShouldHandle = new PredicateBuilder() - .Handle() - .HandleResult(r => r is null), - FallbackAction = args => - { - var avatar = UserAvatar.GetRandomAvatar(); - return Outcome.FromResultAsValueTask(UserAvatar.Blank); - }, - OnFallback = args => - { - // Add extra logic to be executed when the fallback is triggered, such as logging. - return default; // Returns an empty ValueTask - } - }); + var avatar = UserAvatar.GetRandomAvatar(); + return Outcome.FromResultAsValueTask(UserAvatar.Blank); + }, + OnFallback = static args => + { + // Add extra logic to be executed when the fallback is triggered, such as logging. + return default; // Returns an empty ValueTask + } +}; + +// Add a fallback strategy with a FallbackStrategyOptions instance to the pipeline +new ResiliencePipelineBuilder().AddFallback(optionsOnFallback); ``` @@ -311,45 +329,47 @@ For more details, visit the [fallback strategy](https://www.pollydocs.org/strate ```cs -// Add hedging with default options. +// Hedging with default options. // See https://www.pollydocs.org/strategies/hedging#defaults for defaults. -new ResiliencePipelineBuilder() - .AddHedging(new HedgingStrategyOptions()); +var optionsDefaults = new HedgingStrategyOptions(); -// Add a customized hedging strategy that retries up to 3 times if the execution +// A customized hedging strategy that retries up to 3 times if the execution // takes longer than 1 second or if it fails due to an exception or returns an HTTP 500 Internal Server Error. -new ResiliencePipelineBuilder() - .AddHedging(new HedgingStrategyOptions +var optionsComplex = new HedgingStrategyOptions +{ + ShouldHandle = new PredicateBuilder() + .Handle() + .HandleResult(response => response.StatusCode == HttpStatusCode.InternalServerError), + MaxHedgedAttempts = 3, + Delay = TimeSpan.FromSeconds(1), + ActionGenerator = static args => { - ShouldHandle = new PredicateBuilder() - .Handle() - .HandleResult(response => response.StatusCode == HttpStatusCode.InternalServerError), - MaxHedgedAttempts = 3, - Delay = TimeSpan.FromSeconds(1), - ActionGenerator = args => - { - Console.WriteLine("Preparing to execute hedged action."); + Console.WriteLine("Preparing to execute hedged action."); - // Return a delegate function to invoke the original action with the action context. - // Optionally, you can also create a completely new action to be executed. - return () => args.Callback(args.ActionContext); - } - }); + // Return a delegate function to invoke the original action with the action context. + // Optionally, you can also create a completely new action to be executed. + return () => args.Callback(args.ActionContext); + } +}; // Subscribe to hedging events. -new ResiliencePipelineBuilder() - .AddHedging(new HedgingStrategyOptions +var optionsOnHedging = new HedgingStrategyOptions +{ + OnHedging = static args => { - OnHedging = args => - { - Console.WriteLine($"OnHedging: Attempt number {args.AttemptNumber}"); - return default; - } - }); + Console.WriteLine($"OnHedging: Attempt number {args.AttemptNumber}"); + return default; + } +}; + +// Add a hedging strategy with a HedgingStrategyOptions instance to the pipeline +new ResiliencePipelineBuilder().AddHedging(optionsDefaults); ``` -If all hedged attempts fail, the hedging strategy will either re-throw the last exception or return the final failed result to the caller. For more details, visit the [hedging strategy](https://www.pollydocs.org/strategies/hedging) documentation. +If all hedged attempts fail, the hedging strategy will either re-throw the original exception or return the original failed result to the caller. + +For more details, visit the [hedging strategy](https://www.pollydocs.org/strategies/hedging) documentation. ### Timeout @@ -357,45 +377,46 @@ The timeout resilience strategy assumes delegates you execute support [co-operat ```cs -// Add timeout using the default options. -// See https://www.pollydocs.org/strategies/timeout#defaults for defaults. -new ResiliencePipelineBuilder() - .AddTimeout(new TimeoutStrategyOptions()); - // To add a timeout with a custom TimeSpan duration -new ResiliencePipelineBuilder() - .AddTimeout(TimeSpan.FromSeconds(3)); +new ResiliencePipelineBuilder().AddTimeout(TimeSpan.FromSeconds(3)); + +// Timeout using the default options. +// See https://www.pollydocs.org/strategies/timeout#defaults for defaults. +var optionsDefaults = new TimeoutStrategyOptions(); // To add a timeout using a custom timeout generator function -new ResiliencePipelineBuilder() - .AddTimeout(new TimeoutStrategyOptions +var optionsTimeoutGenerator = new TimeoutStrategyOptions +{ + TimeoutGenerator = static args => { - TimeoutGenerator = args => - { - // Note: the timeout generator supports asynchronous operations - return new ValueTask(TimeSpan.FromSeconds(123)); - } - }); + // Note: the timeout generator supports asynchronous operations + return new ValueTask(TimeSpan.FromSeconds(123)); + } +}; // To add a timeout and listen for timeout events -new ResiliencePipelineBuilder() - .AddTimeout(new TimeoutStrategyOptions +var optionsOnTimeout = new TimeoutStrategyOptions +{ + TimeoutGenerator = static args => { - TimeoutGenerator = args => - { - // Note: the timeout generator supports asynchronous operations - return new ValueTask(TimeSpan.FromSeconds(123)); - }, - OnTimeout = args => - { - Console.WriteLine($"{args.Context.OperationKey}: Execution timed out after {args.Timeout.TotalSeconds} seconds."); - return default; - } - }); + // Note: the timeout generator supports asynchronous operations + return new ValueTask(TimeSpan.FromSeconds(123)); + }, + OnTimeout = static args => + { + Console.WriteLine($"{args.Context.OperationKey}: Execution timed out after {args.Timeout.TotalSeconds} seconds."); + return default; + } +}; + +// Add a timeout strategy with a TimeoutStrategyOptions instance to the pipeline +new ResiliencePipelineBuilder().AddTimeout(optionsDefaults); ``` -Timeout strategies throw `TimeoutRejectedException` when a timeout occurs. For more details, visit the [timeout strategy](https://www.pollydocs.org/strategies/timeout) documentation. +Timeout strategies throw `TimeoutRejectedException` when a timeout occurs. + +For more details, visit the [timeout strategy](https://www.pollydocs.org/strategies/timeout) documentation. ### Rate Limiter @@ -412,15 +433,18 @@ new ResiliencePipelineBuilder() // Create a rate limiter that allows 100 executions per minute. new ResiliencePipelineBuilder() - .AddRateLimiter(new SlidingWindowRateLimiter(new SlidingWindowRateLimiterOptions - { - PermitLimit = 100, - Window = TimeSpan.FromMinutes(1) - })); + .AddRateLimiter(new SlidingWindowRateLimiter( + new SlidingWindowRateLimiterOptions + { + PermitLimit = 100, + Window = TimeSpan.FromMinutes(1) + })); ``` -Rate limiter strategy throws `RateLimiterRejectedException` if execution is rejected. For more details, visit the [rate limiter strategy](https://www.pollydocs.org/strategies/rate-limiter) documentation. +Rate limiter strategy throws `RateLimiterRejectedException` if execution is rejected. + +For more details, visit the [rate limiter strategy](https://www.pollydocs.org/strategies/rate-limiter) documentation. ## Next steps diff --git a/docs/getting-started.md b/docs/getting-started.md index 38d3494187e..123328a253b 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -19,7 +19,7 @@ ResiliencePipeline pipeline = new ResiliencePipelineBuilder() .Build(); // Builds the resilience pipeline // Execute the pipeline asynchronously -await pipeline.ExecuteAsync(static async cancellationToken => { /*Your custom logic here */ }, cancellationToken); +await pipeline.ExecuteAsync(static async token => { /*Your custom logic goes here */ }, cancellationToken); ``` @@ -46,7 +46,7 @@ services.AddResiliencePipeline("my-pipeline", builder => }); // Build the service provider -IServiceProvider serviceProvider = services.BuildServiceProvider(); +var serviceProvider = services.BuildServiceProvider(); // Retrieve ResiliencePipelineProvider that caches and dynamically creates the resilience pipelines var pipelineProvider = serviceProvider.GetRequiredService>(); @@ -57,7 +57,7 @@ ResiliencePipeline pipeline = pipelineProvider.GetPipeline("my-pipeline"); // Execute the pipeline await pipeline.ExecuteAsync(static async token => { - // Your custom logic here + // Your custom logic goes here }); ``` diff --git a/docs/strategies/circuit-breaker.md b/docs/strategies/circuit-breaker.md index 50ff7a5b7e6..e3bcfb9ad0e 100644 --- a/docs/strategies/circuit-breaker.md +++ b/docs/strategies/circuit-breaker.md @@ -20,38 +20,38 @@ ```cs -// Add circuit breaker with default options. +// Circuit breaker with default options. // See https://www.pollydocs.org/strategies/circuit-breaker#defaults for defaults. -new ResiliencePipelineBuilder().AddCircuitBreaker(new CircuitBreakerStrategyOptions()); +var optionsDefaults = new CircuitBreakerStrategyOptions(); -// Add circuit breaker with customized options: -// +// Circuit breaker with customized options: // The circuit will break if more than 50% of actions result in handled exceptions, // within any 10-second sampling duration, and at least 8 actions are processed. -new ResiliencePipelineBuilder().AddCircuitBreaker(new CircuitBreakerStrategyOptions +var optionsComplex = new CircuitBreakerStrategyOptions { FailureRatio = 0.5, SamplingDuration = TimeSpan.FromSeconds(10), MinimumThroughput = 8, BreakDuration = TimeSpan.FromSeconds(30), ShouldHandle = new PredicateBuilder().Handle() -}); +}; // Handle specific failed results for HttpResponseMessage: -new ResiliencePipelineBuilder() - .AddCircuitBreaker(new CircuitBreakerStrategyOptions - { - ShouldHandle = new PredicateBuilder() - .Handle() - .HandleResult(response => response.StatusCode == HttpStatusCode.InternalServerError) - }); +var optionsShouldHandle = new CircuitBreakerStrategyOptions +{ + ShouldHandle = new PredicateBuilder() + .Handle() + .HandleResult(response => response.StatusCode == HttpStatusCode.InternalServerError) +}; // Monitor the circuit state, useful for health reporting: var stateProvider = new CircuitBreakerStateProvider(); +var optionsStateProvider = new CircuitBreakerStrategyOptions +{ + StateProvider = stateProvider +}; -new ResiliencePipelineBuilder() - .AddCircuitBreaker(new() { StateProvider = stateProvider }) - .Build(); +var circuitState = stateProvider.CircuitState; /* CircuitState.Closed - Normal operation; actions are executed. @@ -62,16 +62,20 @@ CircuitState.Isolated - Circuit is manually held open; actions are blocked. // Manually control the Circuit Breaker state: var manualControl = new CircuitBreakerManualControl(); - -new ResiliencePipelineBuilder() - .AddCircuitBreaker(new() { ManualControl = manualControl }) - .Build(); +var optionsManualControl = new CircuitBreakerStrategyOptions +{ + ManualControl = manualControl +}; // Manually isolate a circuit, e.g., to isolate a downstream service. await manualControl.IsolateAsync(); // Manually close the circuit to allow actions to be executed again. await manualControl.CloseAsync(); + +// Add a circuit breaker strategy with a CircuitBreakerStrategyOptions{} instance to the pipeline +new ResiliencePipelineBuilder().AddCircuitBreaker(optionsDefaults); +new ResiliencePipelineBuilder().AddCircuitBreaker(optionsStateProvider); ``` diff --git a/docs/strategies/fallback.md b/docs/strategies/fallback.md index a501bba6769..e7f110539fb 100644 --- a/docs/strategies/fallback.md +++ b/docs/strategies/fallback.md @@ -12,48 +12,48 @@ ```cs -// Add a fallback/substitute value if an operation fails. -new ResiliencePipelineBuilder() - .AddFallback(new FallbackStrategyOptions - { - ShouldHandle = new PredicateBuilder() - .Handle() - .HandleResult(r => r is null), - FallbackAction = args => Outcome.FromResultAsValueTask(UserAvatar.Blank) - }); +// A fallback/substitute value if an operation fails. +var optionsSubstitute = new FallbackStrategyOptions +{ + ShouldHandle = new PredicateBuilder() + .Handle() + .HandleResult(r => r is null), + FallbackAction = static args => Outcome.FromResultAsValueTask(UserAvatar.Blank) +}; // Use a dynamically generated value if an operation fails. -new ResiliencePipelineBuilder() - .AddFallback(new FallbackStrategyOptions +var optionsFallbackAction = new FallbackStrategyOptions +{ + ShouldHandle = new PredicateBuilder() + .Handle() + .HandleResult(r => r is null), + FallbackAction = static args => { - ShouldHandle = new PredicateBuilder() - .Handle() - .HandleResult(r => r is null), - FallbackAction = args => - { - var avatar = UserAvatar.GetRandomAvatar(); - return Outcome.FromResultAsValueTask(avatar); - } - }); + var avatar = UserAvatar.GetRandomAvatar(); + return Outcome.FromResultAsValueTask(avatar); + } +}; // Use a default or dynamically generated value, and execute an additional action if the fallback is triggered. -new ResiliencePipelineBuilder() - .AddFallback(new FallbackStrategyOptions +var optionsOnFallback = new FallbackStrategyOptions +{ + ShouldHandle = new PredicateBuilder() + .Handle() + .HandleResult(r => r is null), + FallbackAction = static args => { - ShouldHandle = new PredicateBuilder() - .Handle() - .HandleResult(r => r is null), - FallbackAction = args => - { - var avatar = UserAvatar.GetRandomAvatar(); - return Outcome.FromResultAsValueTask(UserAvatar.Blank); - }, - OnFallback = args => - { - // Add extra logic to be executed when the fallback is triggered, such as logging. - return default; // Returns an empty ValueTask - } - }); + var avatar = UserAvatar.GetRandomAvatar(); + return Outcome.FromResultAsValueTask(UserAvatar.Blank); + }, + OnFallback = static args => + { + // Add extra logic to be executed when the fallback is triggered, such as logging. + return default; // Returns an empty ValueTask + } +}; + +// Add a fallback strategy with a FallbackStrategyOptions instance to the pipeline +new ResiliencePipelineBuilder().AddFallback(optionsOnFallback); ``` diff --git a/docs/strategies/hedging.md b/docs/strategies/hedging.md index 17dd9cf21ef..7783537b40b 100644 --- a/docs/strategies/hedging.md +++ b/docs/strategies/hedging.md @@ -19,41 +19,41 @@ This strategy also supports multiple [concurrency modes](#concurrency-modes) for ```cs -// Add hedging with default options. +// Hedging with default options. // See https://www.pollydocs.org/strategies/hedging#defaults for defaults. -new ResiliencePipelineBuilder() - .AddHedging(new HedgingStrategyOptions()); +var optionsDefaults = new HedgingStrategyOptions(); -// Add a customized hedging strategy that retries up to 3 times if the execution +// A customized hedging strategy that retries up to 3 times if the execution // takes longer than 1 second or if it fails due to an exception or returns an HTTP 500 Internal Server Error. -new ResiliencePipelineBuilder() - .AddHedging(new HedgingStrategyOptions +var optionsComplex = new HedgingStrategyOptions +{ + ShouldHandle = new PredicateBuilder() + .Handle() + .HandleResult(response => response.StatusCode == HttpStatusCode.InternalServerError), + MaxHedgedAttempts = 3, + Delay = TimeSpan.FromSeconds(1), + ActionGenerator = static args => { - ShouldHandle = new PredicateBuilder() - .Handle() - .HandleResult(response => response.StatusCode == HttpStatusCode.InternalServerError), - MaxHedgedAttempts = 3, - Delay = TimeSpan.FromSeconds(1), - ActionGenerator = args => - { - Console.WriteLine("Preparing to execute hedged action."); + Console.WriteLine("Preparing to execute hedged action."); - // Return a delegate function to invoke the original action with the action context. - // Optionally, you can also create a completely new action to be executed. - return () => args.Callback(args.ActionContext); - } - }); + // Return a delegate function to invoke the original action with the action context. + // Optionally, you can also create a completely new action to be executed. + return () => args.Callback(args.ActionContext); + } +}; // Subscribe to hedging events. -new ResiliencePipelineBuilder() - .AddHedging(new HedgingStrategyOptions +var optionsOnHedging = new HedgingStrategyOptions +{ + OnHedging = static args => { - OnHedging = args => - { - Console.WriteLine($"OnHedging: Attempt number {args.AttemptNumber}"); - return default; - } - }); + Console.WriteLine($"OnHedging: Attempt number {args.AttemptNumber}"); + return default; + } +}; + +// Add a hedging strategy with a HedgingStrategyOptions instance to the pipeline +new ResiliencePipelineBuilder().AddHedging(optionsDefaults); ``` diff --git a/docs/strategies/rate-limiter.md b/docs/strategies/rate-limiter.md index 9fc87a86f25..54d6193b57b 100644 --- a/docs/strategies/rate-limiter.md +++ b/docs/strategies/rate-limiter.md @@ -33,11 +33,12 @@ new ResiliencePipelineBuilder() // Create a rate limiter that allows 100 executions per minute. new ResiliencePipelineBuilder() - .AddRateLimiter(new SlidingWindowRateLimiter(new SlidingWindowRateLimiterOptions - { - PermitLimit = 100, - Window = TimeSpan.FromMinutes(1) - })); + .AddRateLimiter(new SlidingWindowRateLimiter( + new SlidingWindowRateLimiterOptions + { + PermitLimit = 100, + Window = TimeSpan.FromMinutes(1) + })); ``` diff --git a/docs/strategies/retry.md b/docs/strategies/retry.md index 3b62c4ba275..22ff071dd85 100644 --- a/docs/strategies/retry.md +++ b/docs/strategies/retry.md @@ -14,32 +14,32 @@ ```cs -// Add retry using the default options. +// Retry using the default options. // See https://www.pollydocs.org/strategies/retry#defaults for defaults. -new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions()); +var optionsDefaults = new RetryStrategyOptions(); // For instant retries with no delay -new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions +var optionsNoDelay = new RetryStrategyOptions { Delay = TimeSpan.Zero -}); +}; // For advanced control over the retry behavior, including the number of attempts, // delay between retries, and the types of exceptions to handle. -new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions +var optionsComplex = new RetryStrategyOptions { ShouldHandle = new PredicateBuilder().Handle(), BackoffType = DelayBackoffType.Exponential, UseJitter = true, // Adds a random factor to the delay MaxRetryAttempts = 4, Delay = TimeSpan.FromSeconds(3), -}); +}; // To use a custom function to generate the delay for retries -new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions +var optionsDelayGenerator = new RetryStrategyOptions { MaxRetryAttempts = 2, - DelayGenerator = args => + DelayGenerator = static args => { var delay = args.AttemptNumber switch { @@ -52,12 +52,12 @@ new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions // but the API also supports asynchronous implementations. return new ValueTask(delay); } -}); +}; // To extract the delay from the result object -new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions +var optionsExtractDelay = new RetryStrategyOptions { - DelayGenerator = args => + DelayGenerator = static args => { if (args.Outcome.Result is HttpResponseMessage responseMessage && TryGetDelay(responseMessage, out TimeSpan delay)) @@ -68,26 +68,30 @@ new ResiliencePipelineBuilder().AddRetry(new RetryStrategyO // Returning null means the retry strategy will use its internal delay for this attempt. return new ValueTask((TimeSpan?)null); } -}); +}; // To get notifications when a retry is performed -new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions +var optionsOnRetry = new RetryStrategyOptions { MaxRetryAttempts = 2, - OnRetry = args => + OnRetry = static args => { Console.WriteLine("OnRetry, Attempt: {0}", args.AttemptNumber); // Event handlers can be asynchronous; here, we return an empty ValueTask. return default; } -}); +}; // To keep retrying indefinitely or until success use int.MaxValue. -new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions +var optionsIndefiniteRetry = new RetryStrategyOptions { MaxRetryAttempts = int.MaxValue, -}); +}; + +// Add a retry strategy with a RetryStrategyOptions{} instance to the pipeline +new ResiliencePipelineBuilder().AddRetry(optionsDefaults); +new ResiliencePipelineBuilder().AddRetry(optionsExtractDelay); ``` diff --git a/docs/strategies/timeout.md b/docs/strategies/timeout.md index 38b5110211b..d2c41009d02 100644 --- a/docs/strategies/timeout.md +++ b/docs/strategies/timeout.md @@ -14,41 +14,40 @@ ```cs -// Add timeout using the default options. -// See https://www.pollydocs.org/strategies/timeout#defaults for defaults. -new ResiliencePipelineBuilder() - .AddTimeout(new TimeoutStrategyOptions()); - // To add a timeout with a custom TimeSpan duration -new ResiliencePipelineBuilder() - .AddTimeout(TimeSpan.FromSeconds(3)); +new ResiliencePipelineBuilder().AddTimeout(TimeSpan.FromSeconds(3)); + +// Timeout using the default options. +// See https://www.pollydocs.org/strategies/timeout#defaults for defaults. +var optionsDefaults = new TimeoutStrategyOptions(); // To add a timeout using a custom timeout generator function -new ResiliencePipelineBuilder() - .AddTimeout(new TimeoutStrategyOptions +var optionsTimeoutGenerator = new TimeoutStrategyOptions +{ + TimeoutGenerator = static args => { - TimeoutGenerator = args => - { - // Note: the timeout generator supports asynchronous operations - return new ValueTask(TimeSpan.FromSeconds(123)); - } - }); + // Note: the timeout generator supports asynchronous operations + return new ValueTask(TimeSpan.FromSeconds(123)); + } +}; // To add a timeout and listen for timeout events -new ResiliencePipelineBuilder() - .AddTimeout(new TimeoutStrategyOptions +var optionsOnTimeout = new TimeoutStrategyOptions +{ + TimeoutGenerator = static args => + { + // Note: the timeout generator supports asynchronous operations + return new ValueTask(TimeSpan.FromSeconds(123)); + }, + OnTimeout = static args => { - TimeoutGenerator = args => - { - // Note: the timeout generator supports asynchronous operations - return new ValueTask(TimeSpan.FromSeconds(123)); - }, - OnTimeout = args => - { - Console.WriteLine($"{args.Context.OperationKey}: Execution timed out after {args.Timeout.TotalSeconds} seconds."); - return default; - } - }); + Console.WriteLine($"{args.Context.OperationKey}: Execution timed out after {args.Timeout.TotalSeconds} seconds."); + return default; + } +}; + +// Add a timeout strategy with a TimeoutStrategyOptions instance to the pipeline +new ResiliencePipelineBuilder().AddTimeout(optionsDefaults); ``` diff --git a/src/Polly.RateLimiting/README.md b/src/Polly.RateLimiting/README.md index 3314daf49a1..986a86e76ee 100644 --- a/src/Polly.RateLimiting/README.md +++ b/src/Polly.RateLimiting/README.md @@ -23,10 +23,11 @@ new ResiliencePipelineBuilder() // Create a rate limiter that allows 100 executions per minute. new ResiliencePipelineBuilder() - .AddRateLimiter(new SlidingWindowRateLimiter(new SlidingWindowRateLimiterOptions - { - PermitLimit = 100, - Window = TimeSpan.FromMinutes(1) - })); + .AddRateLimiter(new SlidingWindowRateLimiter( + new SlidingWindowRateLimiterOptions + { + PermitLimit = 100, + Window = TimeSpan.FromMinutes(1) + })); ``` diff --git a/src/Snippets/Docs/CircuitBreaker.cs b/src/Snippets/Docs/CircuitBreaker.cs index eba48b75dde..b4990c9231c 100644 --- a/src/Snippets/Docs/CircuitBreaker.cs +++ b/src/Snippets/Docs/CircuitBreaker.cs @@ -11,38 +11,38 @@ public static async Task Usage() { #region circuit-breaker - // Add circuit breaker with default options. + // Circuit breaker with default options. // See https://www.pollydocs.org/strategies/circuit-breaker#defaults for defaults. - new ResiliencePipelineBuilder().AddCircuitBreaker(new CircuitBreakerStrategyOptions()); + var optionsDefaults = new CircuitBreakerStrategyOptions(); - // Add circuit breaker with customized options: - // + // Circuit breaker with customized options: // The circuit will break if more than 50% of actions result in handled exceptions, // within any 10-second sampling duration, and at least 8 actions are processed. - new ResiliencePipelineBuilder().AddCircuitBreaker(new CircuitBreakerStrategyOptions + var optionsComplex = new CircuitBreakerStrategyOptions { FailureRatio = 0.5, SamplingDuration = TimeSpan.FromSeconds(10), MinimumThroughput = 8, BreakDuration = TimeSpan.FromSeconds(30), ShouldHandle = new PredicateBuilder().Handle() - }); + }; // Handle specific failed results for HttpResponseMessage: - new ResiliencePipelineBuilder() - .AddCircuitBreaker(new CircuitBreakerStrategyOptions - { - ShouldHandle = new PredicateBuilder() - .Handle() - .HandleResult(response => response.StatusCode == HttpStatusCode.InternalServerError) - }); + var optionsShouldHandle = new CircuitBreakerStrategyOptions + { + ShouldHandle = new PredicateBuilder() + .Handle() + .HandleResult(response => response.StatusCode == HttpStatusCode.InternalServerError) + }; // Monitor the circuit state, useful for health reporting: var stateProvider = new CircuitBreakerStateProvider(); + var optionsStateProvider = new CircuitBreakerStrategyOptions + { + StateProvider = stateProvider + }; - new ResiliencePipelineBuilder() - .AddCircuitBreaker(new() { StateProvider = stateProvider }) - .Build(); + var circuitState = stateProvider.CircuitState; /* CircuitState.Closed - Normal operation; actions are executed. @@ -53,10 +53,10 @@ public static async Task Usage() // Manually control the Circuit Breaker state: var manualControl = new CircuitBreakerManualControl(); - - new ResiliencePipelineBuilder() - .AddCircuitBreaker(new() { ManualControl = manualControl }) - .Build(); + var optionsManualControl = new CircuitBreakerStrategyOptions + { + ManualControl = manualControl + }; // Manually isolate a circuit, e.g., to isolate a downstream service. await manualControl.IsolateAsync(); @@ -64,6 +64,10 @@ public static async Task Usage() // Manually close the circuit to allow actions to be executed again. await manualControl.CloseAsync(); + // Add a circuit breaker strategy with a CircuitBreakerStrategyOptions{} instance to the pipeline + new ResiliencePipelineBuilder().AddCircuitBreaker(optionsDefaults); + new ResiliencePipelineBuilder().AddCircuitBreaker(optionsStateProvider); + #endregion } diff --git a/src/Snippets/Docs/Fallback.cs b/src/Snippets/Docs/Fallback.cs index 74ead75f911..710b749c39e 100644 --- a/src/Snippets/Docs/Fallback.cs +++ b/src/Snippets/Docs/Fallback.cs @@ -11,48 +11,48 @@ public static void Usage() { #region fallback - // Add a fallback/substitute value if an operation fails. - new ResiliencePipelineBuilder() - .AddFallback(new FallbackStrategyOptions - { - ShouldHandle = new PredicateBuilder() - .Handle() - .HandleResult(r => r is null), - FallbackAction = args => Outcome.FromResultAsValueTask(UserAvatar.Blank) - }); + // A fallback/substitute value if an operation fails. + var optionsSubstitute = new FallbackStrategyOptions + { + ShouldHandle = new PredicateBuilder() + .Handle() + .HandleResult(r => r is null), + FallbackAction = static args => Outcome.FromResultAsValueTask(UserAvatar.Blank) + }; // Use a dynamically generated value if an operation fails. - new ResiliencePipelineBuilder() - .AddFallback(new FallbackStrategyOptions + var optionsFallbackAction = new FallbackStrategyOptions + { + ShouldHandle = new PredicateBuilder() + .Handle() + .HandleResult(r => r is null), + FallbackAction = static args => { - ShouldHandle = new PredicateBuilder() - .Handle() - .HandleResult(r => r is null), - FallbackAction = args => - { - var avatar = UserAvatar.GetRandomAvatar(); - return Outcome.FromResultAsValueTask(avatar); - } - }); + var avatar = UserAvatar.GetRandomAvatar(); + return Outcome.FromResultAsValueTask(avatar); + } + }; // Use a default or dynamically generated value, and execute an additional action if the fallback is triggered. - new ResiliencePipelineBuilder() - .AddFallback(new FallbackStrategyOptions + var optionsOnFallback = new FallbackStrategyOptions + { + ShouldHandle = new PredicateBuilder() + .Handle() + .HandleResult(r => r is null), + FallbackAction = static args => { - ShouldHandle = new PredicateBuilder() - .Handle() - .HandleResult(r => r is null), - FallbackAction = args => - { - var avatar = UserAvatar.GetRandomAvatar(); - return Outcome.FromResultAsValueTask(UserAvatar.Blank); - }, - OnFallback = args => - { - // Add extra logic to be executed when the fallback is triggered, such as logging. - return default; // Returns an empty ValueTask - } - }); + var avatar = UserAvatar.GetRandomAvatar(); + return Outcome.FromResultAsValueTask(UserAvatar.Blank); + }, + OnFallback = static args => + { + // Add extra logic to be executed when the fallback is triggered, such as logging. + return default; // Returns an empty ValueTask + } + }; + + // Add a fallback strategy with a FallbackStrategyOptions instance to the pipeline + new ResiliencePipelineBuilder().AddFallback(optionsOnFallback); #endregion } diff --git a/src/Snippets/Docs/Hedging.cs b/src/Snippets/Docs/Hedging.cs index 649a3243515..d41db196fab 100644 --- a/src/Snippets/Docs/Hedging.cs +++ b/src/Snippets/Docs/Hedging.cs @@ -13,41 +13,41 @@ public static void Usage() { #region hedging - // Add hedging with default options. + // Hedging with default options. // See https://www.pollydocs.org/strategies/hedging#defaults for defaults. - new ResiliencePipelineBuilder() - .AddHedging(new HedgingStrategyOptions()); + var optionsDefaults = new HedgingStrategyOptions(); - // Add a customized hedging strategy that retries up to 3 times if the execution + // A customized hedging strategy that retries up to 3 times if the execution // takes longer than 1 second or if it fails due to an exception or returns an HTTP 500 Internal Server Error. - new ResiliencePipelineBuilder() - .AddHedging(new HedgingStrategyOptions + var optionsComplex = new HedgingStrategyOptions + { + ShouldHandle = new PredicateBuilder() + .Handle() + .HandleResult(response => response.StatusCode == HttpStatusCode.InternalServerError), + MaxHedgedAttempts = 3, + Delay = TimeSpan.FromSeconds(1), + ActionGenerator = static args => { - ShouldHandle = new PredicateBuilder() - .Handle() - .HandleResult(response => response.StatusCode == HttpStatusCode.InternalServerError), - MaxHedgedAttempts = 3, - Delay = TimeSpan.FromSeconds(1), - ActionGenerator = args => - { - Console.WriteLine("Preparing to execute hedged action."); + Console.WriteLine("Preparing to execute hedged action."); - // Return a delegate function to invoke the original action with the action context. - // Optionally, you can also create a completely new action to be executed. - return () => args.Callback(args.ActionContext); - } - }); + // Return a delegate function to invoke the original action with the action context. + // Optionally, you can also create a completely new action to be executed. + return () => args.Callback(args.ActionContext); + } + }; // Subscribe to hedging events. - new ResiliencePipelineBuilder() - .AddHedging(new HedgingStrategyOptions + var optionsOnHedging = new HedgingStrategyOptions + { + OnHedging = static args => { - OnHedging = args => - { - Console.WriteLine($"OnHedging: Attempt number {args.AttemptNumber}"); - return default; - } - }); + Console.WriteLine($"OnHedging: Attempt number {args.AttemptNumber}"); + return default; + } + }; + + // Add a hedging strategy with a HedgingStrategyOptions instance to the pipeline + new ResiliencePipelineBuilder().AddHedging(optionsDefaults); #endregion } diff --git a/src/Snippets/Docs/RateLimiter.cs b/src/Snippets/Docs/RateLimiter.cs index b0e91e65b91..4a9785f4ad3 100644 --- a/src/Snippets/Docs/RateLimiter.cs +++ b/src/Snippets/Docs/RateLimiter.cs @@ -21,11 +21,12 @@ public static void Usage() // Create a rate limiter that allows 100 executions per minute. new ResiliencePipelineBuilder() - .AddRateLimiter(new SlidingWindowRateLimiter(new SlidingWindowRateLimiterOptions - { - PermitLimit = 100, - Window = TimeSpan.FromMinutes(1) - })); + .AddRateLimiter(new SlidingWindowRateLimiter( + new SlidingWindowRateLimiterOptions + { + PermitLimit = 100, + Window = TimeSpan.FromMinutes(1) + })); #endregion diff --git a/src/Snippets/Docs/Readme.cs b/src/Snippets/Docs/Readme.cs index 9f14aa8f98a..d37b9c1e37e 100644 --- a/src/Snippets/Docs/Readme.cs +++ b/src/Snippets/Docs/Readme.cs @@ -19,7 +19,7 @@ public static async Task QuickStart() .Build(); // Builds the resilience pipeline // Execute the pipeline asynchronously - await pipeline.ExecuteAsync(static async cancellationToken => { /*Your custom logic here */ }, cancellationToken); + await pipeline.ExecuteAsync(static async token => { /*Your custom logic goes here */ }, cancellationToken); #endregion } @@ -39,7 +39,7 @@ public static async Task QuickStartDi() }); // Build the service provider - IServiceProvider serviceProvider = services.BuildServiceProvider(); + var serviceProvider = services.BuildServiceProvider(); // Retrieve ResiliencePipelineProvider that caches and dynamically creates the resilience pipelines var pipelineProvider = serviceProvider.GetRequiredService>(); @@ -50,7 +50,7 @@ public static async Task QuickStartDi() // Execute the pipeline await pipeline.ExecuteAsync(static async token => { - // Your custom logic here + // Your custom logic goes here }); #endregion diff --git a/src/Snippets/Docs/Retry.cs b/src/Snippets/Docs/Retry.cs index fc3d4cecb65..d1a50f03d4b 100644 --- a/src/Snippets/Docs/Retry.cs +++ b/src/Snippets/Docs/Retry.cs @@ -17,32 +17,32 @@ public static void Usage() { #region retry - // Add retry using the default options. + // Retry using the default options. // See https://www.pollydocs.org/strategies/retry#defaults for defaults. - new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions()); + var optionsDefaults = new RetryStrategyOptions(); // For instant retries with no delay - new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions + var optionsNoDelay = new RetryStrategyOptions { Delay = TimeSpan.Zero - }); + }; // For advanced control over the retry behavior, including the number of attempts, // delay between retries, and the types of exceptions to handle. - new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions + var optionsComplex = new RetryStrategyOptions { ShouldHandle = new PredicateBuilder().Handle(), BackoffType = DelayBackoffType.Exponential, UseJitter = true, // Adds a random factor to the delay MaxRetryAttempts = 4, Delay = TimeSpan.FromSeconds(3), - }); + }; // To use a custom function to generate the delay for retries - new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions + var optionsDelayGenerator = new RetryStrategyOptions { MaxRetryAttempts = 2, - DelayGenerator = args => + DelayGenerator = static args => { var delay = args.AttemptNumber switch { @@ -55,12 +55,12 @@ public static void Usage() // but the API also supports asynchronous implementations. return new ValueTask(delay); } - }); + }; // To extract the delay from the result object - new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions + var optionsExtractDelay = new RetryStrategyOptions { - DelayGenerator = args => + DelayGenerator = static args => { if (args.Outcome.Result is HttpResponseMessage responseMessage && TryGetDelay(responseMessage, out TimeSpan delay)) @@ -71,26 +71,30 @@ public static void Usage() // Returning null means the retry strategy will use its internal delay for this attempt. return new ValueTask((TimeSpan?)null); } - }); + }; // To get notifications when a retry is performed - new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions + var optionsOnRetry = new RetryStrategyOptions { MaxRetryAttempts = 2, - OnRetry = args => + OnRetry = static args => { Console.WriteLine("OnRetry, Attempt: {0}", args.AttemptNumber); // Event handlers can be asynchronous; here, we return an empty ValueTask. return default; } - }); + }; // To keep retrying indefinitely or until success use int.MaxValue. - new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions + var optionsIndefiniteRetry = new RetryStrategyOptions { MaxRetryAttempts = int.MaxValue, - }); + }; + + // Add a retry strategy with a RetryStrategyOptions{} instance to the pipeline + new ResiliencePipelineBuilder().AddRetry(optionsDefaults); + new ResiliencePipelineBuilder().AddRetry(optionsExtractDelay); #endregion } diff --git a/src/Snippets/Docs/Timeout.cs b/src/Snippets/Docs/Timeout.cs index 5fd38d42127..fc436266f35 100644 --- a/src/Snippets/Docs/Timeout.cs +++ b/src/Snippets/Docs/Timeout.cs @@ -9,41 +9,40 @@ public static async Task Usage() { #region timeout - // Add timeout using the default options. - // See https://www.pollydocs.org/strategies/timeout#defaults for defaults. - new ResiliencePipelineBuilder() - .AddTimeout(new TimeoutStrategyOptions()); - // To add a timeout with a custom TimeSpan duration - new ResiliencePipelineBuilder() - .AddTimeout(TimeSpan.FromSeconds(3)); + new ResiliencePipelineBuilder().AddTimeout(TimeSpan.FromSeconds(3)); + + // Timeout using the default options. + // See https://www.pollydocs.org/strategies/timeout#defaults for defaults. + var optionsDefaults = new TimeoutStrategyOptions(); // To add a timeout using a custom timeout generator function - new ResiliencePipelineBuilder() - .AddTimeout(new TimeoutStrategyOptions + var optionsTimeoutGenerator = new TimeoutStrategyOptions + { + TimeoutGenerator = static args => { - TimeoutGenerator = args => - { - // Note: the timeout generator supports asynchronous operations - return new ValueTask(TimeSpan.FromSeconds(123)); - } - }); + // Note: the timeout generator supports asynchronous operations + return new ValueTask(TimeSpan.FromSeconds(123)); + } + }; // To add a timeout and listen for timeout events - new ResiliencePipelineBuilder() - .AddTimeout(new TimeoutStrategyOptions + var optionsOnTimeout = new TimeoutStrategyOptions + { + TimeoutGenerator = static args => + { + // Note: the timeout generator supports asynchronous operations + return new ValueTask(TimeSpan.FromSeconds(123)); + }, + OnTimeout = static args => { - TimeoutGenerator = args => - { - // Note: the timeout generator supports asynchronous operations - return new ValueTask(TimeSpan.FromSeconds(123)); - }, - OnTimeout = args => - { - Console.WriteLine($"{args.Context.OperationKey}: Execution timed out after {args.Timeout.TotalSeconds} seconds."); - return default; - } - }); + Console.WriteLine($"{args.Context.OperationKey}: Execution timed out after {args.Timeout.TotalSeconds} seconds."); + return default; + } + }; + + // Add a timeout strategy with a TimeoutStrategyOptions instance to the pipeline + new ResiliencePipelineBuilder().AddTimeout(optionsDefaults); #endregion