asp.net-web-apiasp.net-web-api2asp.net-core-webapiwebapiretrypolicy

RetryPolicy on Web Api causes timeout


I have a Web Api that invokes another web api call to get some information. In order to make the app more resilient, I implemented a HttpTransientErrorDetectionStrategy following the steps at: https://alexandrebrisebois.wordpress.com/2013/02/21/defining-an-http-transient-error-detection-strategy-for-rest-calls/

After that, I use code like below to invoke the other web app:

RetryPolicy _retryPolicy = new RetryPolicy<HttpTransientErrorDetectionStrategy>(
    new ExponentialBackoff(retryCount: 2, minBackoff: TimeSpan.FromSeconds(0), maxBackoff: TimeSpan.FromSeconds(10), deltaBackoff: TimeSpan.FromSeconds(2)));

var _httpClient = new HttpClient
{
    BaseAddress = new Uri("http://www.microsoft.com")
};

HttpResponseMessage response = _retryPolicy.ExecuteAsync(async () => await _httpClient.GetAsync($"", HttpCompletionOption.ResponseContentRead)).Result;

The _httpClient.GetAsync call gets stuck, and I have no idea why. If I remove the _retryPolicy, and just use _httpClient.GetAsync directly, it returns in a matter of seconds.

I have similar code on a console app, to invoke the same web app, and that is working fine, so this seems to be specific to the way I am using it in my web API. This is intended to be an app on Azure, but it happens when I debug locally as well. Does anybody have any idea why this is getting stuck? How can I debug this?

Thank you!


Solution

  • I have similar code on a console app, to invoke the same web app, and that is working fine, so this seems to be specific to the way I am using it in my web API.

    The code you posted is blocking right here:

    HttpResponseMessage response = _retryPolicy.ExecuteAsync(...).Result;
    

    Don't block on async code. Instead, use await:

    HttpResponseMessage response = await _retryPolicy.ExecuteAsync(...);
    

    If I remove the _retryPolicy, and just use _httpClient.GetAsync directly, it returns in a matter of seconds.

    If your original code is blocking, and you must block on asynchronous code (for some reason), then you can either use the ConfigureAwait(false) hack:

    HttpResponseMessage response = _retryPolicy.ExecuteAsync(async () => await _httpClient.GetAsync($"", HttpCompletionOption.ResponseContentRead).ConfigureAwait(false)).Result;
    

    or elide async/await:

    HttpResponseMessage response = _retryPolicy.ExecuteAsync(() => _httpClient.GetAsync($"", HttpCompletionOption.ResponseContentRead)).Result;
    

    P.S. Check out DecorrelatedJitterBackoffV2.