Feedback provided by Dylan Reisenberge, from Polly's organization.
We'll refactor this when possible.
Only number 1 is really desirable to change (when want to make shutdown graceful). The rest are good to have.
**1. Consider Polly's Execute/ExecuteAsync() overloads taking CancellationToken to facilitate graceful shutdown.
DETAIL:
1. WORTH CONSIDERING Graceful shutdown by CancellationToken: At the moment, all Polly usages are Execute/ExecuteAsync() overloads not taking a CancellationToken. Using instead Execute/Async() overloads taking a CancellationToken might be useful when implementing graceful shutdown. Using the Execute(, CancellationToken) overloads, any requests in the 2/4/8/16/32-second wait parts of exponential backoff will exit as soon as the CancellationToken is signalled (throwing usual OCE). Our microservice projects use this: a central CancellationToken is signalled for graceful shutdown, and all pending requests/operations cancel promptly (and can log they canceled etc). The CancellationToken passed to Polly's Execute/Async(...) can also be passed on to the delegate you execute, eg: policy.ExecuteAsync(ct => apiClient.PutAsync(/* other params*/, ct), someCancellationToken);
2. (OPTION) FallbackPolicy: Where the code has catch(BrokenCircuitException) in the controller (eg https://github.com/dotnet-architecture/eShopOnContainers/blob/0c317d56f3c8937f6823cf1b45f5683397274815/src/Web/WebMVC/Controllers/OrderController.cs#L52-L55 ): you have also the option of Polly's in-built Fallback Policy (https://github.com/App-vNext/Polly/wiki/Fallback). The concept is like:
Policy.Handle<BrokenCircuitException>().FallBack(() => ModelState.AddModelError("Error", "It was not possible to create a new order, please try later on")); // executes the Fallback action if the policy catches the exception.
and you wrap that FallbackPolicy outermost in the PolicyWrap. You could take the Polly dependency entirely out of OrderController, and here (https://github.com/dotnet-architecture/eShopOnContainers/blob/0c317d56f3c8937f6823cf1b45f5683397274815/src/Web/WebMVC/Controllers/OrderController.cs#L46) do something like:
await _orderSvc.CreateOrder(model, onError: () => ModelState.AddModelError("Error", "It was not possible to create a new order, please try later on"));
and pass the Fallback Action onError down to ResilientHttpClient. Not saying this is is preferable. Advantages and disadvantages: adds the fallback Action parameter to the chain of calls; and would change of signatures of your IHttpClient methods, to differ from System.Net.HttpClient.
3. (OPTION) HandleResult
Policy
.Handle
.OrResult(r => r.StatusCode == StatusCode.InternalServerError)
.WaitAndRetryAsync( /* as before */ )
An implication: To use .HandleResult
4. (TIDY UP) Logging from Polly's execution context: Here (https://github.com/dotnet-architecture/eShopOnContainers/blob/0c317d56f3c8937f6823cf1b45f5683397274815/src/Web/WebMVC/Infrastructure/ResilientHttpClientFactory.cs#L36-l37) the code logs from Polly's context.ExecutionKey and context.PolicyKey. At the moment the code isn't setting them to anything. These properties are great though for making logging capture particulars of the call context, when you are using policies centrally like the code does in ResilientHttpClient and EventBusRabbitMQ . ResilientHttpClient and EventBusRabbitMQ () (and similars) could set context.PolicyKey on the policies. context.ExecutionKey could be set (by something higher up the call chain) to indicate which operation is involved (eg GetOrder, GetMyOrders, CreateOrder), either passed in or set on some context flowing with the async calls. Reference: https://github.com/App-vNext/Polly/wiki/Keys-and-Context-Data . Or remove references to context.ExecutionKey and context.PolicyKey if not setting them.
:+1: @CESARDELATORRE . Come back to me if anyone wants any elaboration, nearer the time.
Re 4 above: If/when the new form of ResilientHttpClient in dev branch is merged, then this line:
return policyWrapper.ExecuteAsync(() => action());
can be changed very simply to set Polly's ExecutionKey for the execution, based on the origin variable where you capture origin of the call, thus:
return policyWrapper.ExecuteAsync(() => action(), new Context(origin));
Then your rich logging here will reflect that origin of the call, for everything the Polly policies log.
Closed as it was included in the Roadmap (vNext).
Most helpful comment
Re 4 above: If/when the new form of
ResilientHttpClientin dev branch is merged, then this line:return policyWrapper.ExecuteAsync(() => action());can be changed very simply to set Polly's
ExecutionKeyfor the execution, based on theoriginvariable where you capture origin of the call, thus:return policyWrapper.ExecuteAsync(() => action(), new Context(origin));Then your rich logging here will reflect that
originof the call, for everything the Polly policies log.