-1

I'm sending data to a databricks api with a bad bearer token to test a Polly retry policy, but I get this exception:

InvalidOperation exception: The request message was already sent. Cannot send the same request message multiple times.

enter image description here

HttpResponseMessage response = null;

response = await Policy
 .HandleResult<HttpResponseMessage>(message => !message.IsSuccessStatusCode)
    .WaitAndRetryAsync(3, i => TimeSpan.FromSeconds(2), (result, timeSpan, retryCount, context) =>
    {
        Console.WriteLine($"Request failed with {result.Result.StatusCode}. Waiting {timeSpan} before next retry. Retry attempt {retryCount}");
    })
    .ExecuteAsync(() => httpClient.SendAsync(request));

/***********************************
 * End retry from
 * https://www.jerriepelser.com/blog/retry-network-requests-with-polly/
 * ********************************/


// HttpResponseMessage response = await httpClient.SendAsync(request);
// response = await httpClient.SendAsync(request);                                             

resultContent = await response.Content.ReadAsStringAsync();
response.EnsureSuccessStatusCode();
Peter Csala
  • 17,736
  • 16
  • 35
  • 75
  • This exception is thrown by the HttpClient, not by Polly. You either need to create a new `HttpRequestMessage` for each and every attempt or you can have to use some custom logic, which can [clone it](https://stackoverflow.com/questions/18000583/re-send-httprequestmessage-exception). – Peter Csala Dec 14 '21 at 07:34
  • You should consider using [HttpClientFactory together with Polly](https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests) and let it manage retry policies. The error is clear - you can't reuse an HttpRequestMessage, you'll have to create a new one. HttpClientFactory, when configured to use Polly, takes care of this. – Panagiotis Kanavos Dec 14 '21 at 07:42
  • 1
    Polly has specific [HTTP Policy extensions](https://github.com/App-vNext/Polly.Extensions.Http) that can be used to create retry policies even if you don't use HttpClientFactory, eg `var policy = HttpPolicyExtensions.HandleTransientHttpError().RetryAsync(3);`. Your code becomes a lot cleaner if you use both together though. – Panagiotis Kanavos Dec 14 '21 at 07:45
  • @PanagiotisKanavos The HandleTransientHttpError triggers for 408, 5xx statuscodes or when HttpRequestException is thrown. The OP's policy definition is different. – Peter Csala Dec 14 '21 at 09:04
  • @PeterCsala that's not the point. There are a lot more convenience methods in the HTTP Policy Extensions. – Panagiotis Kanavos Dec 14 '21 at 09:18
  • @PanagiotisKanavos I do believe the OP is experimenting, creating a POC since his whole retry logic does not make sense in this context. If the Authorization token is malformed / expired a retry will not fix the problem. Even though the suggested lib is more handy for Http requests, understanding the fundamentals of Polly is essential for both building custom policies or using predefined ones. – Peter Csala Dec 14 '21 at 09:30
  • Polly and HttpClientFactory work by adding a `PolicyHttpMessageHandler` to `HttpClient` that takes care of retries and request messages. [Handlers can be added to any HttpClient](https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/httpclient-message-handlers) to change its behavior but the easiest way is through `HttpClientFactory.Create` – Panagiotis Kanavos Dec 14 '21 at 09:32

1 Answers1

0

An HttpRequestMessage can't be reused. To send the same request you'd have to clone the original message. Polly handles HTTP retries by adding its own delegating handler to HttpClient instances

Using Polly with Dependency Injection

It's far easier to retry HttpClient calls by using HttpClientFactory together with Polly and dependency injection. Polly offers several policy convenience methods in the Http Policy Extensions package, so specifying retry policies for HttpClient becomes a lot easier.

The following example from the docs specifies that the HttpClients used by the BasketService class

class BasketService:IBasketService
{
    public BasketService(HttpClient client)...
}

will have exponential backoff in case of transient errors :

services.AddHttpClient<IBasketService, BasketService>()
        .SetHandlerLifetime(TimeSpan.FromMinutes(5))  //Set lifetime to five minutes
        .AddPolicyHandler(GetRetryPolicy());

...
static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
    return HttpPolicyExtensions
        .HandleTransientHttpError()
        .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
        .WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2,
                                                                    retryAttempt)));
}

The question's policy would be :

static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
    return Policy
 .HandleResult<HttpResponseMessage>(message => !message.IsSuccessStatusCode)
    .WaitAndRetryAsync(3, i => TimeSpan.FromSeconds(2), (result, timeSpan, retryCount, context) =>
    {
        Console.WriteLine($"Request failed with {result.Result.StatusCode}. Waiting {timeSpan} before next retry. Retry attempt {retryCount}");
    });
}

Without DI

Polly handles HttpClient retries by adding a PolicyMessageHandler to an HttpClient's pipeline. An HttpClient allows customizing calls by allowing handlers to intercept and modify requests and responses.

One could do the same and create an HttpClient that uses PolicyMessageHandler to retry calls. The easiest way to do this is through HttpClientFactory.Create :

var retryPolicy=GetRetryPolicy();
this.httpClient=HttpClientFactory(new PolicyMessageHandler(retryPolicy));
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • Thanks Panagiotis and Pete, I will work on your advice and get back to the thread if I have anymore questions and then upvote when I get it working. – Salvatore Allegra Dec 14 '21 at 11:32
  • I'm using .net 5, nothing is working, going to have to research Polly for .net 5 – Salvatore Allegra Dec 14 '21 at 15:27
  • How is Polly internally able to retry the same `HttpRequestMessage`, even inside a `DelegateHandler`, without hitting an `InvalidOperationException`? – silkfire Apr 17 '23 at 20:04