Polly timeout policy clarification
Asked Answered
P

1

15

I am trying to get the timeout policy to work correctly. I have the following requirements while integrating an api.

  1. Create an http request to invoke endpoint1 and pass the transactionID and capture the result
  2. if the http request does not receive an answer in 20 seconds then send a cancel request with the same transactionID and capture the result

For this task I would like to use Polly which seems to me a fantastic component to help handling transient failures. However as I am very new to this technology I just want to be sure if I am implementing correctly.

First of all I have created a timeout policy with Polly like this

var timeoutPolicy =
    Policy.TimeoutAsync(
        TimeSpan.FromSeconds( 20 ),
        TimeoutStrategy.Optimistic,
        async ( context, timespan, task ) => {
            //write here the cancel request 
        } );

then after that I am ready to execute the policy

var policyResult = await timeoutPolicy.ExecuteAndCaptureAsync( async () => {
    //make here the request 1
} );

What I got from the documentation is that if a timeout occurs inside the timeoutPolicy.ExecuteAndCaptureAsync delegate Polly automagically invoke the onTimeout delegate. Right?

However my questions are:

  • What happens if inside the execute delegate an exception occurs? Should I wrap that polly construct in a try catch?
  • When I analyze the policy result how do I understand if the timeout has happened or not?
Pharyngo answered 14/4, 2017 at 18:8 Comment(0)
P
11

What I got from the documentation is that if a timeout occurs inside the ExecuteAndCaptureAsync delegate Polly automagically invoke the onTimeout delegate. Right?

Correct.

What happens if inside the execute delegate an exception occurs?

Because you are using ExecuteAndCaptureAsync(...), the exception is placed in policyResult.FinalException.

Should I wrap that polly construct in a try catch?

Because you are using ExecuteAndCaptureAsync(..), the exception is placed in policyResult.FinalException, so you don't need a try-catch.

When I analyze the policy result how do I understand if the timeout has happened or not?

TimeoutPolicy throws TimeoutRejectedException on a timeout. Because you are using ExecuteAndCaptureAsync(...), you should find that exception placed in policyResult.FinalException.


A couple of further comments. With TimeoutStrategy.Optimisitic, which is based on co-operative cancellation by CancellationToken, you should execute a delegate taking a cancellation token:

var policyResult = await timeoutPolicy.ExecuteAndCaptureAsync(async (ct) => {
    //make request 1, in a form which responds to the cancellation token ct
}, userCancellationToken /* CancellationToken.None is acceptable. Polly will merge its timing-out CancellationToken into ct, during policy execution. */
);

Second, as an alternative to invoking the cancel request inside the onRetryAsync: async ( context, timespan, task ) => { ... }, you have the option to make the code more sequential / less nested with a pattern like below:

var policyResult = await timeoutPolicy.ExecuteAndCaptureAsync(async (ct) => {
    //make request 1, in a form which responds to the cancellation token ct
}, CancellationToken.None);

if (policyResult.Outcome == OutcomeType.Failure && policyResult.FinalException is TimeoutRejectedException)
{
    //write here the cancel request 
}

UPDATE: Invoking the cancel request will work either way - from inside the onRetryAsync, or sequentially, as just above. An advantage of the sequential version is that it may make it easier to reason about what happens if the cancel request fails with an exception. With the nested approach (cancel request invoked inside onRetryAsync), an exception finally captured into policyResult.FinalException could come from either the initial request or the cancel request - and it may be hard to tell which.

Pavier answered 14/4, 2017 at 19:1 Comment(2)
Hi, thank you very much for your very clear answer. Also I would like to understand how to retrieve the result of my http request call and return it back. The documentation says that in the policyResult variable I should use a Result member but this is not available to me.Pharyngo
PolicyResult.Result is available when the generic method overload .ExecuteAndCaptureAsync<TResult>(...) is used; TResult is the return type of the func you are executing. If the func returns TResult, the compiler should select the correct .ExecuteAndCaptureAsync<TResult>(...) overload for you. If not, try forcing it by explicitly stating .ExecuteAndCaptureAsync<TResult>(...) for your TResult. Spec here github.com/App-vNext/Polly/blob/… shows it working, incl. compiler overload inference.Pavier

© 2022 - 2024 — McMap. All rights reserved.