Understanding the semantics of Polly policies when separating policy definition from execution
Asked Answered
U

1

7

With Polly I'd like to have my policy definition and the execution of that policy in two different statements, as in:

// Policy definition
var policy = Policy
   .HandleResult<IRestResponse>(predicate)
   .Retry(2);

// Policy execution
policy.ExecuteAndCapture(() =>
{
    DoSomethingAndReturnAnIRestResponse();
};

I want to do it this way so I can get better reuse of my retry policies, e.g. for use in dependency injection.

I'm trying to understand if there's any considerations when splitting up the policy and the execution in this way, for example if there's any "state" (for lack of a better term) that might not carry in the policy object from policy definition to execution.

Along these lines I'm noticing that when I use Polly's ExecuteAndCapture() in the above fashion that certain properties (those related to capturing the final exception/outcome, associated with ExecuteAndCapture()) aren't showing on the policy object. Per the documentation (here and here), upon completion of a policy such as the below:

var policy = Policy
   .HandleResult<IRestResponse>(predicate)
   .Retry(2)
   .ExecuteAndCapture(() =>
    {
       DoSomethingAndReturnAnIRestResponse();
    });

...you are supposed to get back:

PolicyResult.Outcome
PolicyResult.FinalException
PolicyResult.ExceptionType
PolicyResult.Result

This indeed is happening then the ExecuteAndCapture() is in the same statement as the policy definition. However, when separating the policy definition from the execution those properties aren't available. I naively assumed they'd show up on the existing policy object, but they don't:

enter image description here

It seems I need to create a new variable assignment in order to access to those properties:

New policy variable with outcome

Any concerns here?

Unrelenting answered 9/6, 2017 at 20:16 Comment(0)
E
14

No concerns. Configuring policies separate from their usage, and injecting them into sites of usage, is a common pattern which we use extensively in production.

All Polly policies are thread-safe and may be used across multiple independent call-sites concurrently.


Two kinds of Polly policy thread-safely maintain internal state across calls, to carry out their designed functions. This leads to specific (intended) effects if you share those policy instances across call sites.

CircuitBreaker / AdvancedCircuitBreaker

The raison-d'etre is to count and act according to success/fail metrics across calls placed through the policy. Each single policy instance maintains this state for itself internally.

The (intended) functional consequence of this is that if you share a CircuitBreakerPolicy instance in multiple call sites, those multiple call sites will share circuit state, as discussed here.

  • Share the same breaker policy instance across call sites when you want those call sites to break in common - for instance they have a common downstream dependency.
  • Don't share a breaker instance across call sites when you want those call sites to have independent circuit state and break independently.

Bulkhead

The raison-d'etre is to limit concurrency of calls placed through it. Each single BulkheadPolicy instance maintains state internally to track that.

The (intended) functional consequence of this is that when you share a BulkheadPolicy instance across call-sites, those call-sites share the bulkhead capacity between them.

  • Share the same BulkheadPolicy instance across multiple call sites when you want call sites to share the bulkhead capacity between them.
  • Don't share the same BulkheadPolicy instance across multiple call sites when you want them to have independent bulkhead capacity.

No other kind of Polly policy maintains internal state in the policy instance across executions.


.ExecuteAndCapture(...)

The result of an .ExecuteAndCapture(...) call is not on the policy in either of the cases in the question. In both cases (definition and execution in one statement; or separated), the result of the .ExecuteAndCapture(...) call is a fresh PolicyResult instance.

Each execution returns a fresh PolicyResult instance. PolicyResult is never stored as state on a policy instance (that would make policies not thread-safe and re-useable across call sites).

Change var to the actual type (Policy or PolicyResult) in each code location, and this may be clearer to see.

Endoscope answered 9/6, 2017 at 21:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.