-1

I have the following code that is supposed to call the ExecuteAsync function from Polly. However, after return objectResponse_.Object; the call does not return to calling function and basically gets lost (events show thread has exited with code 0). I am sure I am doing something wrong with the await async but I can't tell what.

BTW, the Parallel Stacks shows

enter image description here

public async Task < T > SendRequestAsync < T > (HttpRequestMessage request, CancellationToken cancellationToken, bool someBool = true) {

  var retryPolicy = Policy.Handle < Exception > ().Or < HttpRequestException > ().RetryAsync(3, onRetry: (exception, retryCount, context) =>{
    _logger.Log($"API call failed. Doing retry attempt {retryCount}");
  });

  await retryPolicy.ExecuteAsync(async() =>{

    using(var response = await _client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false)) {
      var headers = System.Linq.Enumerable.ToDictionary(response.Headers, h =>h.Key, h =>h.Value);

      if (response.Content ? .Headers != null) {
        foreach(var item in response.Content.Headers) {
          headers[item.Key] = item.Value;
        }
      }

      var status = (int) response.StatusCode;

      switch (status) {

      case 200:
      case 201:
      case 204:
        {
          if (someBool) {
            var objectResponse_ = await ReadObjectResponseAsync < T > (response, headers, cancellationToken).ConfigureAwait(false);
            if (objectResponse_.Object == null) {
              throw new MyException("Response was null which was not expected.", status, objectResponse_.Text, headers, null);
            }
            return objectResponse_.Object;
          }
          else {
            var objectResponse_ = await ReadObjectResponseAsync < T > (response, headers, cancellationToken);
            if (objectResponse_.Object != null) return objectResponse_.Object;
            else return
          default (T);

          }
        }
      case 404:
        {
          if (someBool) {
            var objectResponse_ = await ReadObjectResponseAsync < T > (response, headers, cancellationToken).ConfigureAwait(false);
            return objectResponse_.Object;
          }

          var responseText = (response.Content == null) ? string.Empty: await response.Content.ReadAsStringAsync().ConfigureAwait(false);
          throw new MyException("Not Found", status, responseText, headers, null);
        }
      case 500:
        {
          if (someBool) {
            var objectResponse_ = await ReadObjectResponseAsync < T > (response, headers, cancellationToken).ConfigureAwait(false);
            return objectResponse_.Object;
          }

          var objectResponse = await ReadObjectResponseAsync < MyResponse > (response, headers, cancellationToken).ConfigureAwait(false);
          if (objectResponse.Object == null) {
            throw new MyException("Response was null which was not expected.", status, objectResponse.Text, headers, null);
          }

          throw new MyException < MyResponse > ("Internal Server Error", status, objectResponse.Object.Message, headers, objectResponse.Object, null);
        }

      default:
        {
          var responseData = response.Content == null ? null: await response.Content.ReadAsStringAsync().ConfigureAwait(false);
          throw new MyException("The HTTP status code of the response was not expected (" + status + ").", status, responseData, headers, null);
        }
      }
    }

  });

  return
default (T);

}
Peter Csala
  • 17,736
  • 16
  • 35
  • 75
AIK DO
  • 288
  • 1
  • 4
  • 13
  • Could you please share with us the entire code of `SendRequestAsync` method? Your current code sample does not compile. – Peter Csala Oct 13 '22 at 05:47
  • Based on the code **fragment** that you have shared with us your policy definition needs to be adjusted. Change it from `var retryPolicy = Policy.` to `var retryPolicy = Policy.`. Also use the result of `await retryPolicy.ExecuteAsync(` to return something from `SendRequestAsync`. – Peter Csala Oct 13 '22 at 07:19
  • I added complete code. What do you mean by return something from SendRequestAsync? Do you mean "return await retryPolicy.ExecuteAsync(async () => ..." – AIK DO Oct 13 '22 at 12:50
  • Yes, I meant `return await retryPolicy.ExecuteAsync(`. But let me check the complete source code. – Peter Csala Oct 13 '22 at 12:52

1 Answers1

0

Problems

  1. Whenever you call a return statement inside your async lambda which is provided to the ExecuteAsync you are returning from the async lambda not from the SendRequestAsync

  2. Whenever you call a throw statement inside your async lambda which is provided to the ExecuteAsync you are throwing exception from your async lambda

    • Which will trigger a retry (because of your .Handle<Exception>() clause)
    • Don't know it is intentional or not

Solution for Problem #1

Change your policy to return T

var retryPolicy = Policy<T> //previously Policy
    .Handle<Exception>()
    .Or<HttpRequestException>()
    .RetryAsync(3,
    (exception, retryCount, context) => ...);

And return the result of ExecuteAsync

return await retryPolicy.ExecuteAsync(async () => {

Solution for Problem #2

I assume that whenever you throw a MyException inside your async lambda then you don't want to retry. If that's the case then all you need to do is to add a predicate to the Handle builder method

var retryPolicy = Policy<T> 
    .Handle<Exception>(ex => ex is not MyException)
    .Or<HttpRequestException>()
    .RetryAsync(3,
    (exception, retryCount, context) => ...);
Peter Csala
  • 17,736
  • 16
  • 35
  • 75