I've got the following policy setup:
// Slack api for postMessage is generally 1 per channel per second.
// Some workspace specific limits may apply.
// We just limit everything to 1 second.
var slackApiRateLimitPerChannelPerSecond = 1;
var rateLimit = Policy.RateLimitAsync(slackApiRateLimitPerChannelPerSecond, TimeSpan.FromSeconds(slackApiRateLimitPerChannelPerSecond),
(retryAfter, _) => retryAfter.Add(TimeSpan.FromSeconds(slackApiRateLimitPerChannelPerSecond)));
This should:
I can't wrap my head around wrapping this into a second policy that would retry...
I could retry this like so:
try
{
_policy.Execute(...)
}
catch(RateLimitedException ex)
{
// Policy.Retry with ex.RetryAfter
}
But that does not seem right.
I'd like to retry this a couple (3?) times so the method is abit more resilient - how would i do that?
You can omit the factory and wrap the rate-limiting policy into another one:
var ts = TimeSpan.FromSeconds(1);
var rateLimit = Policy.RateLimit(1, ts);
var policyWrap = Policy.Handle<RateLimitRejectedException>()
.WaitAndRetry(3, _ => ts) // note that you might want to use more advanced back off policy here
.Wrap(rateLimit);
policyWrap.Execute(...);
If you want to respect the returned RetryAfter
then try-catch
approach is way to go, based on the documentation example:
public async Task SearchAsync(string query, HttpContext httpContext)
{
var rateLimit = Policy.RateLimitAsync(20, TimeSpan.FromSeconds(1), 10);
try
{
var result = await rateLimit.ExecuteAsync(() => TextSearchAsync(query));
var json = JsonConvert.SerializeObject(result);
httpContext.Response.ContentType = "application/json";
await httpContext.Response.WriteAsync(json);
}
catch (RateLimitRejectedException ex)
{
string retryAfter = DateTimeOffset.UtcNow
.Add(ex.RetryAfter)
.ToUnixTimeSeconds()
.ToString(CultureInfo.InvariantCulture);
httpContext.Response.StatusCode = 429;
httpContext.Response.Headers["Retry-After"] = retryAfter;
}
}
There is WaitAndRetry
overload with sleepDurationProvider
which also passes the exception, so it can be used for the Wrap
approach:
var policyWrap = Policy.Handle<RateLimitRejectedException>()
.WaitAndRetry(5,
sleepDurationProvider: (_, ex, _) => (ex as RateLimitRejectedException)?.RetryAfter.Add(TimeSpan.From....) ?? TimeSpan.From...,
onRetry:(ex, _, i, _) => { Console.WriteLine($"retry: {i}"); })
.Wrap(rateLimit);