mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-18 12:33:30 +01:00
feat(rest): callbacks for timeout and retry backoff (#11067)
* feat(rest): callbacks for timeout and retry backoff * test: add tests for callback utils * test: fix typo Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com> * fix(retryBackoff): efficient math * docs: minor tweaks * docs: captalisation --------- Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com> Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
This commit is contained in:
@@ -141,7 +141,7 @@ export class BurstHandler implements IHandler {
|
||||
// Since this is not a server side issue, the next request should pass, so we don't bump the retries counter
|
||||
return this.runRequest(routeId, url, options, requestData, retries);
|
||||
} else {
|
||||
const handled = await handleErrors(this.manager, res, method, url, requestData, retries);
|
||||
const handled = await handleErrors(this.manager, res, method, url, requestData, retries, routeId);
|
||||
if (handled === null) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
return this.runRequest(routeId, url, options, requestData, ++retries);
|
||||
|
||||
@@ -420,7 +420,7 @@ export class SequentialHandler implements IHandler {
|
||||
// Since this is not a server side issue, the next request should pass, so we don't bump the retries counter
|
||||
return this.runRequest(routeId, url, options, requestData, retries);
|
||||
} else {
|
||||
const handled = await handleErrors(this.manager, res, method, url, requestData, retries);
|
||||
const handled = await handleErrors(this.manager, res, method, url, requestData, retries, routeId);
|
||||
if (handled === null) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
return this.runRequest(routeId, url, options, requestData, ++retries);
|
||||
|
||||
@@ -5,7 +5,7 @@ import { DiscordAPIError } from '../errors/DiscordAPIError.js';
|
||||
import { HTTPError } from '../errors/HTTPError.js';
|
||||
import { RESTEvents } from '../utils/constants.js';
|
||||
import type { ResponseLike, HandlerRequestData, RouteData } from '../utils/types.js';
|
||||
import { parseResponse, shouldRetry } from '../utils/utils.js';
|
||||
import { normalizeRetryBackoff, normalizeTimeout, parseResponse, shouldRetry, sleep } from '../utils/utils.js';
|
||||
|
||||
let authFalseWarningEmitted = false;
|
||||
|
||||
@@ -65,7 +65,10 @@ export async function makeNetworkRequest(
|
||||
retries: number,
|
||||
) {
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), manager.options.timeout);
|
||||
const timeout = setTimeout(
|
||||
() => controller.abort(),
|
||||
normalizeTimeout(manager.options.timeout, routeId.bucketRoute, requestData.body),
|
||||
);
|
||||
if (requestData.signal) {
|
||||
// If the user signal was aborted, abort the controller, else abort the local signal.
|
||||
// The reason why we don't re-use the user's signal, is because users may use the same signal for multiple
|
||||
@@ -81,6 +84,21 @@ export async function makeNetworkRequest(
|
||||
if (!(error instanceof Error)) throw error;
|
||||
// Retry the specified number of times if needed
|
||||
if (shouldRetry(error) && retries !== manager.options.retries) {
|
||||
const backoff = normalizeRetryBackoff(
|
||||
manager.options.retryBackoff,
|
||||
routeId.bucketRoute,
|
||||
null,
|
||||
retries,
|
||||
requestData.body,
|
||||
);
|
||||
if (backoff === null) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (backoff > 0) {
|
||||
await sleep(backoff);
|
||||
}
|
||||
|
||||
// Retry is handled by the handler upon receiving null
|
||||
return null;
|
||||
}
|
||||
@@ -117,6 +135,7 @@ export async function makeNetworkRequest(
|
||||
* @param url - The fully resolved url to make the request to
|
||||
* @param requestData - Extra data from the user's request needed for errors and additional processing
|
||||
* @param retries - The number of retries this request has already attempted (recursion occurs on the handler)
|
||||
* @param routeId - The generalized API route with literal ids for major parameters
|
||||
* @returns The response if the status code is not handled or null to request a retry
|
||||
*/
|
||||
export async function handleErrors(
|
||||
@@ -126,11 +145,27 @@ export async function handleErrors(
|
||||
url: string,
|
||||
requestData: HandlerRequestData,
|
||||
retries: number,
|
||||
routeId: RouteData,
|
||||
) {
|
||||
const status = res.status;
|
||||
if (status >= 500 && status < 600) {
|
||||
// Retry the specified number of times for possible server side issues
|
||||
if (retries !== manager.options.retries) {
|
||||
const backoff = normalizeRetryBackoff(
|
||||
manager.options.retryBackoff,
|
||||
routeId.bucketRoute,
|
||||
status,
|
||||
retries,
|
||||
requestData.body,
|
||||
);
|
||||
if (backoff === null) {
|
||||
throw new HTTPError(status, res.statusText, method, url, requestData);
|
||||
}
|
||||
|
||||
if (backoff > 0) {
|
||||
await sleep(backoff);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user