Home > database >  Retry the same task multiple times in C# when API returns "too many requests" error
Retry the same task multiple times in C# when API returns "too many requests" error

Time:11-26

I have a generic method for executing an asynchronous tasks in synchronous context with retries.

public static T RunWithRetries<T>(Task<T> task)
{
   while(...) // 3 attempts
   {
       try 
       {
          task.GetAwaiter().GetResult();
       }
       catch 
       {
         // sleep & retry later in case of some exceptions, for example 429
       }
   }
}

Then I'm passing any method from asynchronous API to run it like this.

SyncHelper.RunWithRetries(externalAPI.UploadAsync(fileRequest, fileStream));

The problem is that it works unless an exception happened during the request and we need to re-try. If an error happens, all subsequent retries are also throwing the same exception. So, my questions are

  1. Is this happening because of the fileStream object? It's in the using statement, so it's not being disposed for sure. Can the stream position after first upload attempt be a problem?
  2. Is it normal that the same Task object is being retried? Should I change the way I'm doing it to something better?

CodePudding user response:

Is this happening because of the fileStream object? It's in the using statement, so it's not being disposed for sure. Can the stream position after first upload attempt be a problem?

Yes, this is one of your problems. Whenever a stream is read its Position is not reset to 0 automatically. If you try to re-read it then it will not read anything since the position is at the end of the stream.

So, you either have to create a new stream each and every time or rewind the stream to the beginning.

Is it normal that the same Task object is being retried? Should I change the way I'm doing it to something better?

Whenever a Task has been finished (either with a particular result or with an exception) then re-await-ing or retrieving its Result will not trigger a re-execution. It will simple return the value or the exception.

So, you have to create a new Task for each and every retry attempt. In order to do so you could anticipate a Func<Task<T>> in your RunWithRetries

public static T RunWithRetries<T>(Func<Task<T>> issueRequest)
{
    ...
    issueRequest().GetAwaiter().GetResult();

}

From the caller side it would look like this:

RunWithRetries(() => externalAPI.UploadAsync(fileRequest, new FileStream(...)));
//or
RunWithRetries(() => { fileStream.Position = 0; externalAPI.UploadAsync(fileRequest, fileStream); });
  • Related