Using Polly v8 with HttpClientFactory
In approximately November of 2023 the Polly project moved from v7 to its new v8 implementation. With this new version came a completely different syntax for implementing resilience policies along with a completely changed code object model and a whole new set of types to use. New policies were made available and some were removed and the underlying engine of Polly was streamlined.
I decided to put this quick blog post together as a quick cheat sheet for folks to show how you can quickly move from old V7 to V8 of Polly with minimum friction.
Polly vs Polly.Core
So why this blog? Well the new Polly v8 library contains completely new and breaking syntax format. In order to prevent issues with existing applications the core maintainers of the library decided to split the repo into 2 separate packages:
- Polly — continues to have the old syntax folks know, but also includes a shim wrapper to enable the new syntax. This means that you can in theory try to migrate without changing your library. It also means people who move to the new library will not cause a conflict for other packages using older versions.
- Polly.Core — this is where Polly team want you to get to, it only supports the new v8 syntax is is optimised as such to provide many perf benefits. But it completely cuts you out of the old syntax etc.
For more detailed info please review the Polly team migration guide here
New syntax
Polly v8 completely revamps the syntax for policies, and IMO massively simplifies the implementation.
Take for example the following policy defined in the old Polly syntax:
In the new v8 syntax this simply becomes:
As you can likely see the new syntax is slightly more verbose, but it provides the following benefits:
- There is now a much more fluent chaining of resilience policies.
- PolicyWrap is now intrinsically provided for you under the covers of the
ResiliencePipelineBuilder
. - You no longer have to declare something using the
sync
orasync
as the newResiliencePipelineBuilder
pattern automatically provides both options to us. - Configuration of your pipelines is no longer convoluted by method overloads and can now be done via a simple
<policy>StrategyOptions
pattern where<policy>
refers to the type of resilience policy being added into the pipeline. - The new builder pattern easily helps developers understand the explicit order in which policies will be executed rather than order of parameters passed to convoluted
PolicyWrap
.
Using v8 Polly with HttpClientFactory
Hopefully most of you are using resilience policies with your HttpClient
via the HttpClientBuilder
extensions library provided via the Microsoft.Extensions.Http.Polly
nuget. I won’t go into the details of this but if you’d like to read more then please see this Microsoft learning article.
Unfortunately due to the fact that the v8 version of Polly has all new types and a new builder pattern syntax this will no longer work. So instead you should look to use the new Microsoft.Extensions.Http.Resilience
nuget found here
Assuming you know Polly v8 then the migration path is quite simple. Lets assume you have the following Polly v7 resilience policy:
In the new Polly v8 world using Microsoft.Extensions.Http.Resilience
this will now look something like this:
Things to note:
- When you call
AddCircuitBreaker
in your pipeline you should be using theHttp
prefixed options objects such asHttpCircuitBreakerStrategyOptions
which helps with wiring up the types of exceptions to handle nicely. - The new
HttpCircuitBreakerStrategyOptions
also nicely encapsulate all of the oldHandleTransientHttpError
logic for you too which will check the response message status codes. - If you configure a timeout on the
HttpClient
(as shown above) YOU MUST MANUALLY add a circuit breaker into your resilience pipeline to handle theTaskCanceledException
as the Polly helper code does not see this as a “Transient issue” - The return type of my
GetCircuitBreakerPolicy
method is actuallyvoid
rather than returning a policy or a builder. This is because you must now mutate theResiliencePipelineBuilder
given to you by theAddResilienceHandler
extension by adding your policy definitions. - The
AddResilienceHandler
requires a string name for your policy. This may not make much sense, but it is worth noting that under the hood our policies are added to a concurrent dictionary/registry to allow the builder to quickly retrieve policies and prevent duplication. - There is a handy
AddStandardResilienceHandler
method that can be used which sets you up an opinionated default “best practice” pipeline documented on the nuget page - This
AddStandardResilienceHandler
method you will provide you with a resilience pipeline that has:
— “total timeout” pipeline applies an overall timeout to the execution of all sub policies below, ensuring that the request including hedging attempts, does not exceed the configured limit.
— A retry pipeline retries the request in case the dependency is slow or returns a transient error.
— A rate limiter pipeline limits the maximum number of requests being send to the dependency.
— A circuit breaker blocks the execution if too many direct failures or timeouts are detected.
— A timeout pipeline which limits each request attempt duration and throws if its exceeded.
Thanks for reading, I will do a more in depth blog about the new features of Polly v8 in a future blog.