feat: present x-ratelimit-scope for 429s hit (#9973)

* feat: present x-ratelimit-scope for 429s hit

* fix: get scope from headers for burst too
This commit is contained in:
Vlad Frangu
2023-11-16 12:49:05 +02:00
committed by GitHub
parent 0aa7dc1b86
commit 6df233de14
4 changed files with 19 additions and 1 deletions

View File

@@ -21,6 +21,8 @@ export class RateLimitError extends Error implements RateLimitData {
public sublimitTimeout: number; public sublimitTimeout: number;
public scope: RateLimitData['scope'];
public constructor({ public constructor({
timeToReset, timeToReset,
limit, limit,
@@ -32,6 +34,7 @@ export class RateLimitError extends Error implements RateLimitData {
global, global,
retryAfter, retryAfter,
sublimitTimeout, sublimitTimeout,
scope,
}: RateLimitData) { }: RateLimitData) {
super(); super();
this.timeToReset = timeToReset; this.timeToReset = timeToReset;
@@ -44,6 +47,7 @@ export class RateLimitError extends Error implements RateLimitData {
this.global = global; this.global = global;
this.retryAfter = retryAfter; this.retryAfter = retryAfter;
this.sublimitTimeout = sublimitTimeout; this.sublimitTimeout = sublimitTimeout;
this.scope = scope;
} }
/** /**

View File

@@ -2,7 +2,7 @@ import type { RequestInit } from 'undici';
import type { REST } from '../REST.js'; import type { REST } from '../REST.js';
import type { IHandler } from '../interfaces/Handler.js'; import type { IHandler } from '../interfaces/Handler.js';
import { RESTEvents } from '../utils/constants.js'; import { RESTEvents } from '../utils/constants.js';
import type { ResponseLike, HandlerRequestData, RouteData } from '../utils/types.js'; import type { ResponseLike, HandlerRequestData, RouteData, RateLimitData } from '../utils/types.js';
import { onRateLimit, sleep } from '../utils/utils.js'; import { onRateLimit, sleep } from '../utils/utils.js';
import { handleErrors, incrementInvalidCount, makeNetworkRequest } from './Shared.js'; import { handleErrors, incrementInvalidCount, makeNetworkRequest } from './Shared.js';
@@ -102,6 +102,7 @@ export class BurstHandler implements IHandler {
} else if (status === 429) { } else if (status === 429) {
// Unexpected ratelimit // Unexpected ratelimit
const isGlobal = res.headers.has('X-RateLimit-Global'); const isGlobal = res.headers.has('X-RateLimit-Global');
const scope = (res.headers.get('X-RateLimit-Scope') ?? 'user') as RateLimitData['scope'];
await onRateLimit(this.manager, { await onRateLimit(this.manager, {
global: isGlobal, global: isGlobal,
@@ -114,6 +115,7 @@ export class BurstHandler implements IHandler {
timeToReset: retryAfter, timeToReset: retryAfter,
retryAfter, retryAfter,
sublimitTimeout: 0, sublimitTimeout: 0,
scope,
}); });
this.debug( this.debug(
@@ -128,6 +130,7 @@ export class BurstHandler implements IHandler {
` Limit : ${Number.POSITIVE_INFINITY}`, ` Limit : ${Number.POSITIVE_INFINITY}`,
` Retry After : ${retryAfter}ms`, ` Retry After : ${retryAfter}ms`,
` Sublimit : None`, ` Sublimit : None`,
` Scope : ${scope}`,
].join('\n'), ].join('\n'),
); );

View File

@@ -237,6 +237,7 @@ export class SequentialHandler implements IHandler {
timeToReset: timeout, timeToReset: timeout,
retryAfter: timeout, retryAfter: timeout,
sublimitTimeout: 0, sublimitTimeout: 0,
scope: 'user',
}; };
// Let library users know they have hit a rate limit // Let library users know they have hit a rate limit
@@ -281,6 +282,7 @@ export class SequentialHandler implements IHandler {
const reset = res.headers.get('X-RateLimit-Reset-After'); const reset = res.headers.get('X-RateLimit-Reset-After');
const hash = res.headers.get('X-RateLimit-Bucket'); const hash = res.headers.get('X-RateLimit-Bucket');
const retry = res.headers.get('Retry-After'); const retry = res.headers.get('Retry-After');
const scope = (res.headers.get('X-RateLimit-Scope') ?? 'user') as RateLimitData['scope'];
// Update the total number of requests that can be made before the rate limit resets // Update the total number of requests that can be made before the rate limit resets
this.limit = limit ? Number(limit) : Number.POSITIVE_INFINITY; this.limit = limit ? Number(limit) : Number.POSITIVE_INFINITY;
@@ -359,6 +361,7 @@ export class SequentialHandler implements IHandler {
timeToReset: timeout, timeToReset: timeout,
retryAfter, retryAfter,
sublimitTimeout: sublimitTimeout ?? 0, sublimitTimeout: sublimitTimeout ?? 0,
scope,
}); });
this.debug( this.debug(
@@ -373,6 +376,7 @@ export class SequentialHandler implements IHandler {
` Limit : ${limit}`, ` Limit : ${limit}`,
` Retry After : ${retryAfter}ms`, ` Retry After : ${retryAfter}ms`,
` Sublimit : ${sublimitTimeout ? `${sublimitTimeout}ms` : 'None'}`, ` Sublimit : ${sublimitTimeout ? `${sublimitTimeout}ms` : 'None'}`,
` Scope : ${scope}`,
].join('\n'), ].join('\n'),
); );

View File

@@ -164,6 +164,13 @@ export interface RateLimitData {
* The route being hit in this request * The route being hit in this request
*/ */
route: string; route: string;
/**
* The scope of the rate limit that was hit.
*
* This can be `user` for rate limits that are per client, `global` for rate limits that affect all clients or `shared` for rate limits that
* are shared per resource.
*/
scope: 'global' | 'shared' | 'user';
/** /**
* The time, in milliseconds, that will need to pass before the sublimit lock for the route resets, and requests that fall under a sublimit * 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 * can be retried