Skip to content

Commit

Permalink
feat(client): handle retry-after with a date (#242)
Browse files Browse the repository at this point in the history
  • Loading branch information
stainless-bot committed Sep 25, 2023
1 parent c8b64f9 commit ca21e25
Showing 1 changed file with 24 additions and 19 deletions.
43 changes: 24 additions & 19 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -502,32 +502,37 @@ export abstract class APIClient {
retriesRemaining -= 1;

// About the Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
//
// TODO: we may want to handle the case where the header is using the http-date syntax: "Retry-After: <http-date>".
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After#syntax for details.
const retryAfter = parseInt(responseHeaders?.['retry-after'] || '');
let timeoutMillis: number | undefined;
const retryAfterHeader = responseHeaders?.['retry-after'];
if (retryAfterHeader) {
const timeoutSeconds = parseInt(retryAfterHeader);
if (!Number.isNaN(timeoutSeconds)) {
timeoutMillis = timeoutSeconds * 1000;
} else {
timeoutMillis = Date.parse(retryAfterHeader) - Date.now();
}
}

const maxRetries = options.maxRetries ?? this.maxRetries;
const timeout = this.calculateRetryTimeoutSeconds(retriesRemaining, retryAfter, maxRetries) * 1000;
await sleep(timeout);
// If the API asks us to wait a certain amount of time (and it's a reasonable amount),
// just do what it says, but otherwise calculate a default
if (
!timeoutMillis ||
!Number.isInteger(timeoutMillis) ||
timeoutMillis <= 0 ||
timeoutMillis > 60 * 1000
) {
const maxRetries = options.maxRetries ?? this.maxRetries;
timeoutMillis = this.calculateDefaultRetryTimeoutMillis(retriesRemaining, maxRetries);
}
await sleep(timeoutMillis);

return this.makeRequest(options, retriesRemaining);
}

private calculateRetryTimeoutSeconds(
retriesRemaining: number,
retryAfter: number,
maxRetries: number,
): number {
private calculateDefaultRetryTimeoutMillis(retriesRemaining: number, maxRetries: number): number {
const initialRetryDelay = 0.5;
const maxRetryDelay = 2;

// If the API asks us to wait a certain amount of time (and it's a reasonable amount),
// just do what it says.
if (Number.isInteger(retryAfter) && retryAfter <= 60) {
return retryAfter;
}

const numRetries = maxRetries - retriesRemaining;

// Apply exponential backoff, but not more than the max.
Expand All @@ -536,7 +541,7 @@ export abstract class APIClient {
// Apply some jitter, plus-or-minus half a second.
const jitter = Math.random() - 0.5;

return sleepSeconds + jitter;
return (sleepSeconds + jitter) * 1000;
}

private getUserAgent(): string {
Expand Down

0 comments on commit ca21e25

Please sign in to comment.