Skip to content

Polly v6 breaking changes

martincostello edited this page Sep 28, 2023 · 6 revisions

Polly v6 breaking changes

ℹ️ This documentation describes the previous Polly v7 API. If you are using the new v8 API, please refer to pollydocs.org.

Polly v6 introduced a number of breaking changes over v5.

Change Summary rationale
Rationalise Execute()/ ExecuteAsync() overloads Reduce the number of overloads; standardise to build a more consistent overload set going forward
Rename ExecutionKey as OperationKey Better communicate purpose; reduce confusion with similar-named key
Rename ExecutionGuid as CorrelationId Better communicate purpose; reduce confusion with similar-named key
Publish Polly as strong-named only Avoid diamond-dependency conflicts in the market going forward
Upgrade supported targets Modernise

All .cs changes are deprecated in Polly v5.9 with [Obsolete] attributes.

Rationalise Execute()/ ExecuteAsync() overloads

What changed?

As well as taking the delegate to execute, Polly Execute()/ ExecuteAsync() overloads commonly exist offering the following two parameters:

Parameter Purpose
Context an execution Context, or dictionary data to populate that context
CancellationToken a CancellationToken, when you want to pass a token to control cancellation of the policy or the executing delegate

In Polly v5, some overloads existed where the Context context or CancellationToken parameters were passed but not used as input parameters to the delegate executed:

Task ExecuteAsync(Func<Task> action, Context context)

With such overloads, the passed Context or CancellationToken parameters influenced policy behaviour, but not executed delegate behaviour.

In v6 all such overloads were deprecated in favour of overloads where the action delegate executed takes all passed parameters. For example, the overload quoted above would be deprecated in favour of:

Task ExecuteAsync(Func<Context, Task> action, Context context)

Why?

By aligning the parameters passed to the executed delegate with the input parameters to the executed delegate, we open up the possibility for adding future generic overloads allowing user-defined input parameters to be passed into the executed delegate without the use of closures:

Task<TResult> ExecuteAsync<T1, TResult>(Func<T1, Context, CancellationToken, Task<TResult>> func, T1 t1, Context context, CancellationToken token);

Task<TResult> ExecuteAsync<T1, T2, TResult>(Func<T1, T2, Context, CancellationToken, Task<TResult>> func, T1 t1, T2 t2, Context context, CancellationToken token);

Without the v6 standardisation of overloads, this would not be possible.

How to upgrade to v6

If you receive a compilation error that the overload no longer exists in v6, simply add Context or CancellationToken (as appropriate) as an input to the executed delegate.

Note

ExecuteAsync() overloads also offer the bool continueOnCapturedContext optional parameter. This has never featured as an input parameter to async delegates executed through policies and no changes were made to this.

Rename ExecutionKey as OperationKey; ExecutionGuid as CorrelationId

What changed and why?

The following properties on Context were renamed:

Old name New name
ExecutionKey OperationKey
ExecutionGuid CorrelationId

How to upgrade to v6

Use the new property names.

Publish Polly as strong-named only

What changed?

To v5.9 Polly was published as separate nuget packages Polly (unsigned) and Polly-Signed. In the Polly <=v5.9 context, Signed actually means strong-named.

From Polly v6.0 only a single package, Polly, is published, and it is strong-named.

Why?

ASPNET Core 2.1 offers deep integration with Polly for HttpClient instances created through HttpClientFactory. This allows the easy application of Polly resilience strategies to outbound calls through HttpClient.

ASPNET Core 2.1 is strong-named so this was expected to significantly increase consumption of a strong-named form of Polly. That however would risk creating diamond-dependency conflicts which apparently have no solution for packages or projects which until then had consumed non-strong-named Polly (example) (good summary).

If we had left both non-strong-named and strong-named Polly in the market, we would have prolonged the possibility of these irresoluble conflicts into future months and years. Switching to a single, strong-named Polly limits the possibility of these diamond-dependency conflicts occurring, by there being only a single version in the market. The single strong-named package strategy is also the strategy used by high-download nuget packages such as NewtonSoft.Json, FluentValidation, and Serilog, and is the strategy recommended by the Microsoft ASP.NET team.

From v6.0 Polly also adopts the so-called Newtonsoft.Json versioning strategy described here to minimise onward impact:

  • nuget package numbers follow full semver (Major.Minor.Patch increments) to differentiate bug-fixes, new-functionality-without-breaking-changes, and breaking-changes.
  • Dlls partially follow Major.0.0 version numbering only (even if the true version is Major.Minor.Patch). This reduces the burden on projects which consume Polly of introducing excessive assembly binding redirects.

Reference: Further discussion.

How to upgrade to v6

Switch packages as follows:

Old package used New package to use
Polly-Signed Polly
Polly Polly

Both non-strong-named DLLs and strong-named DLLs can reference strong-named DLLs, so whether your project uses strong-naming or not, you will be able to reference the new Polly v6 strong-named packages.

Clone this wiki locally