-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add README.md for Polly.Core #1060
Merged
Merged
Changes from 2 commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,10 +1,10 @@ | ||||||
# Introduction | ||||||
|
||||||
The Polly V8 API exposes unified and non-allocating resilience API that is described in the sections bellow. | ||||||
The Polly V8 API exposes unified and non-allocating resilience API that is described in the sections below. | ||||||
|
||||||
## Core API | ||||||
|
||||||
At the heart of Polly V8 is the `ResilienceStrategy` class that is responsible for execution of user code. It's one class that handles all Polly V7 scenarios: | ||||||
At the heart of Polly V8 is the [ResilienceStrategy](ResilienceStrategy.cs) class that is responsible for execution of user code. It's one class that handles all Polly V7 scenarios: | ||||||
|
||||||
- `ISyncPolicy` | ||||||
- `IAsyncPolicy` | ||||||
|
@@ -14,8 +14,10 @@ At the heart of Polly V8 is the `ResilienceStrategy` class that is responsible f | |||||
``` csharp | ||||||
public abstract class ResilienceStrategy | ||||||
{ | ||||||
// the main method that all the others call | ||||||
protected virtual ValueTask<TResult> ExecuteCoreAsync<TResult, TState>(Func<ResilienceContext, TState, ValueTask<TResult>> execution, ResilienceContext context, TState state); | ||||||
|
||||||
// convenience methods for various types of user-callbacks | ||||||
public void Execute(Action callback); | ||||||
|
||||||
public TResult Execute<TResult>(Func<TResult> callback); | ||||||
|
@@ -32,7 +34,7 @@ public abstract class ResilienceStrategy | |||||
} | ||||||
``` | ||||||
|
||||||
The `ResilienceContext` is defined as: | ||||||
The [ResilienceContext](ResilienceContext.cs) is defined as: | ||||||
|
||||||
``` csharp | ||||||
public sealed class ResilienceContext | ||||||
|
@@ -45,6 +47,8 @@ public sealed class ResilienceContext | |||||
|
||||||
public bool ContinueOnCapturedContext { get; } | ||||||
|
||||||
public Type ResultType { get; } | ||||||
|
||||||
// omitted for simplicity | ||||||
} | ||||||
``` | ||||||
|
@@ -57,14 +61,15 @@ exposed on this class that cover different scenarios: | |||||
- Asynchronous void methods. | ||||||
- Asynchronous methods with result. | ||||||
|
||||||
For example, synchronous `Execute` method is implemented as: | ||||||
For example, the synchronous `Execute` method is implemented as: | ||||||
|
||||||
``` csharp | ||||||
public void Execute(Action execute) | ||||||
{ | ||||||
var context = ResilienceContext.Get(); | ||||||
|
||||||
context.IsSynchronous = true; | ||||||
context.IsVoid = true; | ||||||
context.ResultType = typeof(VoidResult); | ||||||
|
||||||
try | ||||||
{ | ||||||
|
@@ -85,18 +90,18 @@ public void Execute(Action execute) | |||||
|
||||||
In the preceding example: | ||||||
|
||||||
- We rent `ResilienceContext` from pool. | ||||||
- We store the information about the execution mode by setting the `IsSynchronous` and `IsVoid` properties to the context. | ||||||
- We pass the user delegate, and use the `State` to avoid closure allocation. | ||||||
- We rent a `ResilienceContext` from the pool. | ||||||
- We store the information about the execution mode by setting the `IsSynchronous` and `ResultType` properties on the context. Here, we use internal `VoidResult` marker to say this user-callback returns no result. | ||||||
- We pass the user-callback, and use the `State` to avoid closure allocation. | ||||||
- We block the execution. | ||||||
- We return `ResilienceContext` to the pool. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
Underlying implementation decides how to execute this delegate by reading the `ResilienceContext`: | ||||||
Underlying implementation decides how to execute this user-callback by reading the `ResilienceContext`: | ||||||
|
||||||
``` csharp | ||||||
internal class DelayStrategy : DelegatingResilienceStrategy | ||||||
{ | ||||||
protected override async ValueTask<T> ExecuteCoreAsync<T, TState>(Func<ResilienceContext, TState, ValueTask<T>> execution, ResilienceContext context, TState state) | ||||||
protected override async ValueTask<T> ExecuteCoreAsync<T, TState>(Func<ResilienceContext, TState, ValueTask<T>> callback, ResilienceContext context, TState state) | ||||||
{ | ||||||
if (context.IsSynchronous) | ||||||
{ | ||||||
|
@@ -123,7 +128,7 @@ The life of extensibility author is also simplified as they only maintain one im | |||||
|
||||||
## Creation of `ResilienceStrategy` | ||||||
|
||||||
This API exposes `ResilienceStrategyBuilder` that can be used to create the resilience strategy: | ||||||
This API exposes [ResilienceStrategyBuilder](Builder/ResilienceStrategyBuilder.cs) that can be used to create the resilience strategy: | ||||||
|
||||||
``` csharp | ||||||
public interface ResilienceStrategyBuilder | ||||||
|
@@ -143,7 +148,7 @@ To create a strategy or pipeline of strategies you chain various extensions for | |||||
Single strategy: | ||||||
|
||||||
``` csharp | ||||||
var resilienceStrategy = new ResilienceStrategyBuilder().AddRetry().Create(); | ||||||
var resilienceStrategy = new ResilienceStrategyBuilder().AddRetry().Build(); | ||||||
``` | ||||||
|
||||||
Pipeline of strategies: | ||||||
|
@@ -164,17 +169,17 @@ The resilience extensibility is simple. You just expose extensions for `Resilien | |||||
|
||||||
Various implementations of `ResilienceStrategy` use callbacks to provide or request information from user. The callbacks are generic and support any type of result. Most strategies will use the following types of callbacks: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
- **Predicates**: These return `true` or `false` values based on the input. The input can be the result of user delegate or some exception. For example, determine whether we should retry the user delegate for specific result. | ||||||
- **Events**: These are just events raised when something important happens. For example when timeout occurs. | ||||||
- **Generators**: These generate a value based on the input. For example, retry delay before the next retry attempt. | ||||||
- **Predicates**: These return `true` or `false` values based on the input. The input can be the result of an user-callback or some exception. For example, to determine whether we should retry the user-callback for a specific result. | ||||||
- **Events**: These are events raised when something important happens. For example when a timeout occurs. | ||||||
- **Generators**: These generate a value based on the input. For example, a retry delay before the next retry attempt. | ||||||
|
||||||
All callbacks are asynchronous and return `ValueTask`. They provide the following information to the user: | ||||||
|
||||||
- `ResilienceContext`: the context of the operation. | ||||||
- Result type: for what result type is the strategy being executed. | ||||||
- Callback arguments: Additional information about the event. Using arguments is preferable because it makes the API more stable. If we decide to add a new member to the arguments, the call sites won't break. | ||||||
|
||||||
Each callback type has associated class that can be reused across various strategies. For example see the `Predicates` class and the usage in the `RetryStrategyOptions.ShouldRetry`: | ||||||
Each callback type has an associated class that can be reused across various strategies. For example, see the `Predicates` class and the usage of the `RetryStrategyOptions.ShouldRetry` property: | ||||||
|
||||||
``` csharp | ||||||
public Predicates ShouldRetry { get; set; } = new(); | ||||||
|
@@ -184,21 +189,21 @@ public Predicates ShouldRetry { get; set; } = new(); | |||||
var options = new RetryStrategyOptions(); | ||||||
options | ||||||
.ShouldRetry | ||||||
.Add<HttpResponseMessage>(m => m.StatusCode == HttpStatusCode.InternalServerError) // inspecting the result | ||||||
.Add<HttpResponseMessage>(result => result.StatusCode == HttpStatusCode.InternalServerError) // inspecting the result | ||||||
.Add(HttpStatusCode.InternalServerError) // particular value for other type | ||||||
.Add<MyResult>(v => v.IsError) | ||||||
.Add<MyResult>((v, context) => IsError(context)) // retrieve data from context for evaluation | ||||||
.Add<MyResult>(result => result.IsError) | ||||||
.Add<MyResult>((result, context) => IsError(context)) // retrieve data from context for evaluation | ||||||
.AddException<InvalidOperationException>() // exceptions | ||||||
.AddException<HttpRequestMessageException>() // more exceptions | ||||||
.AddException(e => IsError(e)) // exception predicates | ||||||
.Add<MyResult>((v, context) => await IsErrorAsync(v, context)); // async predicates | ||||||
.AddException(error => IsError(error)) // exception predicates | ||||||
.Add<MyResult>(async (result, context) => await IsErrorAsync(result, context)); // async predicates | ||||||
``` | ||||||
|
||||||
In the preceding sample you see that `ShouldRetry` handles the following scenarios: | ||||||
|
||||||
- Asynchronous predicates. | ||||||
- Synchronous predicates. | ||||||
- Concrete value results. | ||||||
- Custom function-based callbacks. | ||||||
- Different result types. | ||||||
- Exception types or exception-based predicates. | ||||||
- Asynchronous predicates; | ||||||
- Synchronous predicates; | ||||||
- Concrete value results; | ||||||
- Custom function-based callbacks; | ||||||
- Different result types; | ||||||
- Exception types or exception-based predicates; |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In a follow-up PR, I wonder if we should integrate something like MarkdownSnippets.Tool into the repo and have real working/compilable samples in the test project that can auto-generate so these things are easy to keep up-to-date and working as v8 gets implemented?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks really nice and it would help to keep the docs in sync with the code. Love the idea.
Fyi, we are also using
docfx
that supports the similar thing such as referencing snippets from the code using regions:https://dotnet.github.io/docfx/docs/markdown.html?tabs=linux%2Cdotnet#code-snippet
I'll go ahead and merge this and we address this in follow-up.