[Fix] Enforce rate limits on 429s (#2642)

* Reduce available requests to zero on 429

* Update remaining requests when available

* Fix typo

* Use -1 for unlimited

* Update rate limit once per request
This commit is contained in:
Ben Reilly
2023-04-14 19:06:47 -04:00
committed by GitHub
parent 23e27e0b49
commit 69cce5baf2

View File

@@ -1,4 +1,5 @@
using Discord.API; using Discord.API;
using Discord.Net.Rest;
using Newtonsoft.Json; using Newtonsoft.Json;
using System; using System;
#if DEBUG_LIMITS #if DEBUG_LIMITS
@@ -62,10 +63,11 @@ namespace Discord.Net.Queue
#if DEBUG_LIMITS #if DEBUG_LIMITS
Debug.WriteLine($"[{id}] Sending..."); Debug.WriteLine($"[{id}] Sending...");
#endif #endif
RestResponse response = default(RestResponse);
RateLimitInfo info = default(RateLimitInfo); RateLimitInfo info = default(RateLimitInfo);
try try
{ {
var response = await request.SendAsync().ConfigureAwait(false); response = await request.SendAsync().ConfigureAwait(false);
info = new RateLimitInfo(response.Headers, request.Endpoint); info = new RateLimitInfo(response.Headers, request.Endpoint);
request.Options.ExecuteRatelimitCallback(info); request.Options.ExecuteRatelimitCallback(info);
@@ -87,7 +89,6 @@ namespace Discord.Net.Queue
#if DEBUG_LIMITS #if DEBUG_LIMITS
Debug.WriteLine($"[{id}] (!) 429"); Debug.WriteLine($"[{id}] (!) 429");
#endif #endif
UpdateRateLimit(id, request, info, true, body: response.Stream);
} }
await _queue.RaiseRateLimitTriggered(Id, info, $"{request.Method} {request.Endpoint}").ConfigureAwait(false); await _queue.RaiseRateLimitTriggered(Id, info, $"{request.Method} {request.Endpoint}").ConfigureAwait(false);
continue; //Retry continue; //Retry
@@ -156,7 +157,7 @@ namespace Discord.Net.Queue
}*/ }*/
finally finally
{ {
UpdateRateLimit(id, request, info, false); UpdateRateLimit(id, request, info, response.StatusCode == (HttpStatusCode)429, body: response.Stream);
#if DEBUG_LIMITS #if DEBUG_LIMITS
Debug.WriteLine($"[{id}] Stop"); Debug.WriteLine($"[{id}] Stop");
#endif #endif
@@ -251,7 +252,7 @@ namespace Discord.Net.Queue
DateTimeOffset? timeoutAt = request.TimeoutAt; DateTimeOffset? timeoutAt = request.TimeoutAt;
int semaphore = Interlocked.Decrement(ref _semaphore); int semaphore = Interlocked.Decrement(ref _semaphore);
if (windowCount > 0 && semaphore < 0) if (windowCount >= 0 && semaphore < 0)
{ {
if (!isRateLimited) if (!isRateLimited)
{ {
@@ -358,9 +359,16 @@ namespace Discord.Net.Queue
if (info.Limit.HasValue && WindowCount != info.Limit.Value) if (info.Limit.HasValue && WindowCount != info.Limit.Value)
{ {
WindowCount = info.Limit.Value; WindowCount = info.Limit.Value;
_semaphore = is429 ? 0 : info.Remaining.Value;
#if DEBUG_LIMITS #if DEBUG_LIMITS
Debug.WriteLine($"[{id}] Upgraded Semaphore to {info.Remaining.Value}/{WindowCount}"); Debug.WriteLine($"[{id}] Updated Limit to {WindowCount}");
#endif
}
if (info.Remaining.HasValue && _semaphore != info.Remaining.Value)
{
_semaphore = info.Remaining.Value;
#if DEBUG_LIMITS
Debug.WriteLine($"[{id}] Updated Semaphore (Remaining) to {_semaphore}");
#endif #endif
} }
@@ -374,6 +382,9 @@ namespace Discord.Net.Queue
}*/ }*/
if (is429) if (is429)
{ {
// Stop all requests until the QueueReset task is complete
_semaphore = 0;
// use the payload reset after value // use the payload reset after value
var payload = info.ReadRatelimitPayload(body); var payload = info.ReadRatelimitPayload(body);
@@ -439,7 +450,7 @@ namespace Discord.Net.Queue
if (resetTick == null) if (resetTick == null)
{ {
WindowCount = 0; //No rate limit info, disable limits on this bucket WindowCount = -1; //No rate limit info, disable limits on this bucket
#if DEBUG_LIMITS #if DEBUG_LIMITS
Debug.WriteLine($"[{id}] Disabled Semaphore"); Debug.WriteLine($"[{id}] Disabled Semaphore");
#endif #endif