I want to achieve the same behavior as the policy below with the built-in RateLimit policy, i.e. the logger message and to read the Retry-After header and wait the exact seconds that were needed to wait for but using the built-in RateLimit policy.
Attempt
// TODO: No logger message and not sure if it waits the time taken from the Retry-After header.
public static AsyncRateLimitPolicy Limit<T>(ILogger<T> logger)
{
return Policy.RateLimitAsync(RateLimitRetryCount, TimeSpan.FromSeconds(5));
}
Works
public static AsyncRetryPolicy<RestResponse> AsyncRateLimit<T>(ILogger<T> logger)
{
return Policy.HandleResult<RestResponse>(response => response.StatusCode == HttpStatusCode.TooManyRequests)
.WaitAndRetryAsync(RateLimitRetryCount,
(attemptCount, restResponse, _) =>
{
var retryAfterHeader = restResponse?.Result?.Headers?.SingleOrDefault(h => h.Name == "Retry-After");
double secondsInterval = 0;
if (retryAfterHeader != null)
{
var value = retryAfterHeader.Value?.ToString();
if (!double.TryParse(value, out secondsInterval))
{
secondsInterval = Math.Pow(2, attemptCount);
}
}
return TimeSpan.FromSeconds(secondsInterval);
},
(response, timeSpan, retryCount, _) =>
{
logger.LogTrace(
"The API request has been rate limited. HttpStatusCode={StatusCode}. Waiting {Seconds} seconds before retry. Number attempt {RetryCount}. Uri={Url}; RequestResponse={Content}",
response.Result.StatusCode, timeSpan.TotalSeconds, retryCount, response.Result.ResponseUri, response.Result.Content);
return Task.CompletedTask;
});
}