Using the Polly Timeout when making a Http Request

Full source code available here.

When making remote service requests the remote side will sometimes take longer than acceptable to respond. You have a few choices in handling this.

1. Put up with it, but this means you’re stuck waiting for some unknown period and hope you get a response.
2. Wait for the local HttpClient to timeout.
3. Fire off more requests expecting one of them to respond quickly. But now you have multiple requests open holding resources on your side.

Polly offers another approach. The Polly Timeout Policy allows you to specify how long a request should take to respond and if it doesn’t respond in the time period you specify, a cancellation token is used to release held resources.
The Timeout policy can be combined with a retry policy to fire off another request as soon as the timeout occurs.

This is great for GET requests, but be careful with anything that has side effects, search for idempotent if you don’t know what I mean.

Code
I define two policies, the first is a simple Retry Policy that is set to retry three times. The other is a Timeout Policy that is set to react if no response is received within one second.

readonly RetryPolicy<HttpResponseMessage> _httpRetryPolicy;
readonly TimeoutPolicy _timeoutPolicy;

public CatalogController()
{
	_httpRetryPolicy =
		Policy.HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
			.Or<TimeoutRejectedException>()
			.RetryAsync(3);

	_timeoutPolicy = Policy.TimeoutAsync(1);
}

Next I chain (or wrap) these policies around a http request executed by HttpClient.

HttpResponseMessage response = await 
	_httpRetryPolicy.ExecuteAsync(() =>
		_timeoutPolicy.ExecuteAsync(async token => 
			await httpClient.GetAsync(requestEndpoint, token), CancellationToken.None));

Line 4 makes the request to the remote service using the HttpClient.
Line 3, executes the timeout policy, if the http client does NOT respond with 1 second the timeout policy will throw a TimeoutRejectedExcetion.
Line 2, the retry policy condition will trigger when a TimeoutRejectedException occurs, and a retry will be performed.
Line 1, sets the return value from the _httpRetryPolicy to the HttpResponse.

Execution
1. The HttpClient calls the remote inventory service which has been written to take 10 seconds to respond for the first three requests, the fourth replies promptly.
2. For the first three requests, after 1 second no response is received from the remote controller, so the retry policy throws a TimeoutRejectedException and cancels the request by the HttpClient.
3. The retry policy condition is triggered and the a retry is executed.

This sequence repeats itself two more times.

4. Finally, on the fourth attempt the request succeeds in less than a second and the a response is returned.

Conclusion
Timeout lets you preempt a slow response and when combined with a retry policy it allows you to send another request quickly.

Full source code available here.

Returning default values from a failed web request with Polly Fallbacks

Full source code available here.

In previous posts on Polly I showed how to use a simple retry, and a retry with a delegate that is called before the request is retried.

In this post I will show how use a Polly fallback policy, this allows a “fallback” or default value to be returned when both the request and retries fail.

The retry policy is similar to ones I have used before.

_httpRetrytPolicy = Policy.HandleResult<HttpResponseMessage>(
         r => r.StatusCode == HttpStatusCode.InternalServerError)
        .WaitAndRetryAsync(2, retryAttempt => TimeSpan.FromSeconds(retryAttempt));

The fallback policy returns a HttpResponseMessage with some cached values.

_httpRequestFallbackPolicy = Policy.HandleResult<HttpResponseMessage>(
         r => r.StatusCode == HttpStatusCode.InternalServerError)
         .FallbackAsync(new HttpResponseMessage(HttpStatusCode.OK)
         {
             Content = new ObjectContent(_cachedList.GetType(), _cachedList, new JsonMediaTypeFormatter())
         });

Using the polices is almost the same as in my earlier posts, the httpClient.GetAsync is executed by the retry policy. The retry policy is executed by the fallback policy. The Polly documentation refers to this as a “‘Russian-Doll’ or ‘onion-skin-layers’ model”.

HttpResponseMessage httpResponse =  
   await _httpRequestFallbackPolicy.ExecuteAsync( () 
   =>  _httpRetrytPolicy.ExecuteAsync(()
   =>  httpClient.GetAsync(requestEndpoint)));

The above call inside a call can also be done with a Polly policy wrap, I will demonstrate that in later post.

If the request succeeds the first time, the response is assigned to httpResponse. If the request fails, the retry policy kicks in and retries up to three times. If all of those fail, the fallback policy returns a httpResponse containing the cached list.

Full source code available here.

Re-authorization and onRetry with Polly

Full source code available here.

In a previous post I showed how to use Polly to make a simple call, retrying in the event of failure. In another I showed how to let a request fail and process the response.

In this post I will show a retry where a delegate is called as part of the retry. A common use case for this is reauthorizing after an Unauthorized response.

The difference between this policy and the ones in my previous posts is very small.

public class ValuesController : ApiController
{
    readonly RetryPolicy<HttpResponseMessage> _httpRequestPolicy;
    private HttpClient _httpClient;
    public ValuesController()
    {
        _httpRequestPolicy = Policy.HandleResult<HttpResponseMessage>(
                r => r.StatusCode == HttpStatusCode.InternalServerError ||
                     r.StatusCode == HttpStatusCode.Unauthorized)
            .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(retryAttempt), onRetry: (response, timespan) =>
            {
                if (response.Result.StatusCode == HttpStatusCode.Unauthorized)
                {
                    PerformReauthorization();
                }
            });
    }

The onRetry delegate is the new part –

onRetry: (response, timespan) =>
{
	if (response.Result.StatusCode == HttpStatusCode.Unauthorized)
	{
		PerformReauthorization();
	}
}

In my PerformReauthorization I create a new HttpClient and pass it the new authorization code. For simplicity I am passing a code in the cookie of my request, you could of course use Basic Authentication or any another technique.

private void PerformReauthorization()
{
    // here you would get a new token, call some other service, etc.
    _httpClient = GetHttpClient("GoodAuthCode"); // pass in the good auth token
}

The important thing to note is that my HttpClient instance is class level, meaning it can be accessed from PerformReauthorization.

The call to the web service remains the same.

HttpResponseMessage httpResponse = await _httpRequestPolicy.ExecuteAsync(() => _httpClient.GetAsync(requestEndpoint))

I’ll post of fallbacks soon, they allow a default value to be returned in the event of repeated failures.

For completeness here is GetHttpClient.

private HttpClient GetHttpClient(string authCookieValue)
{
    var cookieContainer = new CookieContainer();
    var handler = new HttpClientHandler() {CookieContainer = cookieContainer};
    cookieContainer.Add(new Uri("http://localhost"),new Cookie("Auth", authCookieValue));
    _httpClient = new HttpClient(handler);
    _httpClient.BaseAddress = new Uri(@"http://localhost:2629/api/");

    _httpClient.DefaultRequestHeaders.Accept.Clear();
    _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    return _httpClient;
}

Full source code available here.

Letting a request fail with Polly

Polly is fantastic for transparently retrying a request but there is always a point at which you must give up. Here you have a choice, let the request fail and inform the original caller or use the Fallback feature of Polly. In this post I’m going to show how to let the request fail. I’ll post another time on the Polly Fallback feature.

With Polly, failing is done in much the same was as you would do without Polly. Make the request, examine the response, return the appropriate status to the caller.

The example code below shows this when calling a Web Api 2 endpoint. The hoped for response is OK (200), any other is considered a failure.

HttpResponseMessage httpResponse = await _policies.RequestTimeoutPolicy.ExecuteAsync(() => httpClient.GetAsync(requestEndpoint));

if (httpResponse.IsSuccessStatusCode)
{
    int number = await httpResponse.Content.ReadAsAsync<int>();
    return Ok(number);
}

string errorMessage = await httpResponse.Content.ReadAsStringAsync();
return Content(httpResponse.StatusCode, errorMessage);

Fallback would allow me to return some default number and OK even if the web request failed. Coming soon…