feat: expose Retry-After and sublimit timeouts in RatelimitData (#9864)

* feat: expose Retry-After and sublimit timeouts in RatelimitData

* chore: better docs?

* Apply suggestions from code review

Co-authored-by: ckohen <chaikohen@gmail.com>

---------

Co-authored-by: ckohen <chaikohen@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
Vlad Frangu 2023-11-12 16:36:12 +02:00 committed by GitHub
parent 62e6573296
commit 81e7866903
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 54 additions and 14 deletions

View file

@ -17,7 +17,22 @@ export class RateLimitError extends Error implements RateLimitData {
public global: boolean;
public constructor({ timeToReset, limit, method, hash, url, route, majorParameter, global }: RateLimitData) {
public retryAfter: number;
public sublimitTimeout: number;
public constructor({
timeToReset,
limit,
method,
hash,
url,
route,
majorParameter,
global,
retryAfter,
sublimitTimeout,
}: RateLimitData) {
super();
this.timeToReset = timeToReset;
this.limit = limit;
@ -27,6 +42,8 @@ export class RateLimitError extends Error implements RateLimitData {
this.route = route;
this.majorParameter = majorParameter;
this.global = global;
this.retryAfter = retryAfter;
this.sublimitTimeout = sublimitTimeout;
}
/**

View file

@ -102,16 +102,20 @@ export class BurstHandler implements IHandler {
} else if (status === 429) {
// Unexpected ratelimit
const isGlobal = res.headers.has('X-RateLimit-Global');
await onRateLimit(this.manager, {
timeToReset: retryAfter,
limit: Number.POSITIVE_INFINITY,
global: isGlobal,
method,
hash: this.hash,
url,
route: routeId.bucketRoute,
majorParameter: this.majorParameter,
global: isGlobal,
hash: this.hash,
limit: Number.POSITIVE_INFINITY,
timeToReset: retryAfter,
retryAfter,
sublimitTimeout: 0,
});
this.debug(
[
'Encountered unexpected 429 rate limit',

View file

@ -227,19 +227,23 @@ export class SequentialHandler implements IHandler {
}
const rateLimitData: RateLimitData = {
timeToReset: timeout,
limit,
global: isGlobal,
method: options.method ?? 'get',
hash: this.hash,
url,
route: routeId.bucketRoute,
majorParameter: this.majorParameter,
global: isGlobal,
hash: this.hash,
limit,
timeToReset: timeout,
retryAfter: timeout,
sublimitTimeout: 0,
};
// Let library users know they have hit a rate limit
this.manager.emit(RESTEvents.RateLimited, rateLimitData);
// Determine whether a RateLimitError should be thrown
await onRateLimit(this.manager, rateLimitData);
// When not erroring, emit debug for what is happening
if (isGlobal) {
this.debug(`Global rate limit hit, blocking all requests for ${timeout}ms`);
@ -345,15 +349,18 @@ export class SequentialHandler implements IHandler {
}
await onRateLimit(this.manager, {
timeToReset: timeout,
limit,
global: isGlobal,
method,
hash: this.hash,
url,
route: routeId.bucketRoute,
majorParameter: this.majorParameter,
global: isGlobal,
hash: this.hash,
limit,
timeToReset: timeout,
retryAfter,
sublimitTimeout: sublimitTimeout ?? 0,
});
this.debug(
[
'Encountered unexpected 429 rate limit',
@ -368,6 +375,7 @@ export class SequentialHandler implements IHandler {
` Sublimit : ${sublimitTimeout ? `${sublimitTimeout}ms` : 'None'}`,
].join('\n'),
);
// If caused by a sublimit, wait it out here so other requests on the route can be handled
if (sublimitTimeout) {
// Normally the sublimit queue will not exist, however, if a sublimit is hit while in the sublimit queue, it will

View file

@ -155,12 +155,23 @@ export interface RateLimitData {
* The HTTP method being performed
*/
method: string;
/**
* The time, in milliseconds, that will need to pass before this specific request can be retried
*/
retryAfter: number;
/**
* The route being hit in this request
*/
route: string;
/**
* The time, in milliseconds, until the request-lock is reset
* The time, in milliseconds, that will need to pass before the sublimit lock for the route resets, and requests that fall under a sublimit
* can be retried
*
* This is only present on certain sublimits, and `0` otherwise
*/
sublimitTimeout: number;
/**
* The time, in milliseconds, until the route's request-lock is reset
*/
timeToReset: number;
/**