Skip to content
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

Infrastructure for strategies - async delegates for Events, Predicates, Generators #1067

Closed
martintmk opened this issue Mar 20, 2023 · 2 comments
Assignees
Labels
v8 Issues related to the new version 8 of the Polly library.
Milestone

Comments

@martintmk
Copy link
Contributor

Handling of different result types

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:

  • 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 an associated class that can be reused across various strategies. For example, see the Predicates class and the usage of the RetryStrategyOptions.ShouldRetry property:

public Predicates ShouldRetry { get; set; } = new();
var options = new RetryStrategyOptions();
options
    .ShouldRetry
    .Add<HttpResponseMessage>(result => result.StatusCode == HttpStatusCode.InternalServerError) // inspecting the result
    .Add(HttpStatusCode.InternalServerError) // particular value for other type
    .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(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;

We can just polish these and move to the official branch.

@martintmk martintmk added this to the v8.0.0 milestone Mar 20, 2023
@martintmk martintmk added the v8 Issues related to the new version 8 of the Polly library. label Mar 20, 2023
@PeterCsalaHbo
Copy link

PeterCsalaHbo commented Mar 22, 2023

This asynchronous predicate can be handy in some situations. BUT it might also encourage people to perform database lookups or network requests which could take time.

The current design embraces simple and quick evaluation of trigger firing. With async predicates the consumers of the library might put there complex and time-consuming logic.

I might overthink the tendency of misuse, but the potential is there.

@martintmk
Copy link
Contributor Author

The current design embraces simple and quick evaluation of trigger firing. With async predicates the consumers of the library might put there complex and time-consuming logic.

We encountered use-cases where we needed to do asynchronous work from the delegates. For example:

  • Retrieving the retry delay from the HTTP Response body that is not buffered.
  • Calling external service when circuit breaker is triggered.

The V8 API is fully asynchronous, however it's entirely up to individual strategies to expose the callbacks in the form that make sense (synchronous, asynchronous or both). We will propadly just provide general guidelines and some helpers to archive this. The V8 just opens a new capabilities.

I might overthink the tendency of misuse, but the potential is there.
Definitely, you can even misuse the current synchronous callbacks and do something crazy inside, the same thing applies for V8 :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
v8 Issues related to the new version 8 of the Polly library.
Projects
No open projects
Status: Done
Development

No branches or pull requests

2 participants