Fork me on GitHub

Custom policies for all execution types (Custom policies Part IV)

In the previous articles in this series, we have seen how to author custom reactive and proactive Polly policies:

This article in the series - Part IV - explains (with links to examples) the easiest way to author all forms of a Polly policy, for both sync and async, and both generic and non-generic cases.

The four forms of each Polly policy

For simplicity, Parts II and III of this series assumed we were developing a custom policy for use with Polly and HttpClientFactory. This means that all executions were async and returned Task<HttpResponseMessage>. This meant we only needed to demonstrate the AsyncPolicy<TResult> form of each policy.

Those familiar with Polly will know that policies offered by Polly in fact exist in four forms:

  • synchronous non-generic : Policy
  • synchronous generic : Policy<TResult>
  • asynchronous non-generic : AsyncPolicy
  • asynchronous generic : AsyncPolicy<TResult>

A Polly policy intended to be generally useful (for example, to be offered as a nuget package) should generally support all four of these forms. However, in fact only implementing two forms is necessary:

  • synchronous generic (ie the expected implementation for Policy<TResult>)
  • asynchronous generic (the expected implementation for AsyncPolicy<TResult>)

This blog post explains a pattern for achieving that simplification.

Separate sync and async implementations

It should be relatively obvious that separate sync and async implementations are needed. Async is viral. Async implementations need to be able to execute the Func<Context, CancellationToken, Task<TResult>> passed by the user to policy.ExecuteAsync(...). To do this they have to use await internally.

Synchronous implementations, on the other hand, cannot use await internally.

Common implementations for non-generic and generic policy forms

Consider for a moment the synchronous non-generic Policy (eg RetryPolicy). Users can either (among various related overloads):

  • .Execute(Action); or
  • .Execute<TResult>(Func<TResult>).

Polly avoids you having to write a separate implementation for the Action case by simply executing an Action action through the Func<TResult> implementation, as the equivalent of:

Func<object> wrapper = () => { action(); return ThrowAwaySingleton; }

(In current implementations, in fact an empty struct rather than object is used.)

Similar simplifications exist for the async case.

The net effect of this is that the only implementations needed are:

  • a single TResult-returning implementation for both non-generic and generic synchronous policy forms
  • a single Task<TResult>-returning implementation for both non-generic and generic asynchronous policy forms

Seeing the simplification in practice

A suggested implementation to achieve the simplification is shown in our custom policy templates (and also ready for you to lift off the shelf as a template):

The async files in the same folder as above also demonstrate the same pattern for async policies.

Any policy implementation in the main Polly repo also demonstrates the pattern.

Where next?

This concludes our series on authoring custom policies in Polly.

Grab the template

If you want to dive straight in to experimenting with a custom policy, just grab the template repo and start copy/paste/rename/extend-ing the classes.

For any questions, reach out to the Polly team at Polly github or Polly slack.

If you'd like your custom policy to be part of Polly.Contrib, contact us on Polly slack, we'll have some brief discussion to understand how the policy fits, then we can set up a Polly.Contrib repo - to which you have full rights - to help you manage and deliver your awesomeness to the Polly community!

Happy customising!

Author image
United Kingdom Website
Dylan is the lead contributor and architect on Polly, and the brains behind the roadmap, which is seeing Polly growing into the most robust, flexible and fully-featured resilience library for .NET!