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
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;
/**