Skip to content

Commit

Permalink
feat(client): handle retry-after with a date (#340)
Browse files Browse the repository at this point in the history
  • Loading branch information
stainless-bot authored Sep 25, 2023
1 parent 1bf84b6 commit b6dd384
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 68 deletions.
43 changes: 24 additions & 19 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -509,32 +509,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 @@ -543,7 +548,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
98 changes: 49 additions & 49 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -905,15 +905,15 @@
"@types/yargs-parser" "*"

"@typescript-eslint/eslint-plugin@^6.7.0":
version "6.7.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.2.tgz#f18cc75c9cceac8080a9dc2e7d166008c5207b9f"
integrity sha512-ooaHxlmSgZTM6CHYAFRlifqh1OAr3PAQEwi7lhYhaegbnXrnh7CDcHmc3+ihhbQC7H0i4JF0psI5ehzkF6Yl6Q==
version "6.7.3"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.3.tgz#d98046e9f7102d49a93d944d413c6055c47fafd7"
integrity sha512-vntq452UHNltxsaaN+L9WyuMch8bMd9CqJ3zhzTPXXidwbf5mqqKCVXEuvRZUqLJSTLeWE65lQwyXsRGnXkCTA==
dependencies:
"@eslint-community/regexpp" "^4.5.1"
"@typescript-eslint/scope-manager" "6.7.2"
"@typescript-eslint/type-utils" "6.7.2"
"@typescript-eslint/utils" "6.7.2"
"@typescript-eslint/visitor-keys" "6.7.2"
"@typescript-eslint/scope-manager" "6.7.3"
"@typescript-eslint/type-utils" "6.7.3"
"@typescript-eslint/utils" "6.7.3"
"@typescript-eslint/visitor-keys" "6.7.3"
debug "^4.3.4"
graphemer "^1.4.0"
ignore "^5.2.4"
Expand All @@ -922,31 +922,31 @@
ts-api-utils "^1.0.1"

"@typescript-eslint/parser@^6.7.0":
version "6.7.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.7.2.tgz#e0ae93771441b9518e67d0660c79e3a105497af4"
integrity sha512-KA3E4ox0ws+SPyxQf9iSI25R6b4Ne78ORhNHeVKrPQnoYsb9UhieoiRoJgrzgEeKGOXhcY1i8YtOeCHHTDa6Fw==
dependencies:
"@typescript-eslint/scope-manager" "6.7.2"
"@typescript-eslint/types" "6.7.2"
"@typescript-eslint/typescript-estree" "6.7.2"
"@typescript-eslint/visitor-keys" "6.7.2"
version "6.7.3"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.7.3.tgz#aaf40092a32877439e5957e18f2d6a91c82cc2fd"
integrity sha512-TlutE+iep2o7R8Lf+yoer3zU6/0EAUc8QIBB3GYBc1KGz4c4TRm83xwXUZVPlZ6YCLss4r77jbu6j3sendJoiQ==
dependencies:
"@typescript-eslint/scope-manager" "6.7.3"
"@typescript-eslint/types" "6.7.3"
"@typescript-eslint/typescript-estree" "6.7.3"
"@typescript-eslint/visitor-keys" "6.7.3"
debug "^4.3.4"

"@typescript-eslint/scope-manager@6.7.2":
version "6.7.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.7.2.tgz#cf59a2095d2f894770c94be489648ad1c78dc689"
integrity sha512-bgi6plgyZjEqapr7u2mhxGR6E8WCzKNUFWNh6fkpVe9+yzRZeYtDTbsIBzKbcxI+r1qVWt6VIoMSNZ4r2A+6Yw==
"@typescript-eslint/scope-manager@6.7.3":
version "6.7.3"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.7.3.tgz#07e5709c9bdae3eaf216947433ef97b3b8b7d755"
integrity sha512-wOlo0QnEou9cHO2TdkJmzF7DFGvAKEnB82PuPNHpT8ZKKaZu6Bm63ugOTn9fXNJtvuDPanBc78lGUGGytJoVzQ==
dependencies:
"@typescript-eslint/types" "6.7.2"
"@typescript-eslint/visitor-keys" "6.7.2"
"@typescript-eslint/types" "6.7.3"
"@typescript-eslint/visitor-keys" "6.7.3"

"@typescript-eslint/type-utils@6.7.2":
version "6.7.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.7.2.tgz#ed921c9db87d72fa2939fee242d700561454f367"
integrity sha512-36F4fOYIROYRl0qj95dYKx6kybddLtsbmPIYNK0OBeXv2j9L5nZ17j9jmfy+bIDHKQgn2EZX+cofsqi8NPATBQ==
"@typescript-eslint/type-utils@6.7.3":
version "6.7.3"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.7.3.tgz#c2c165c135dda68a5e70074ade183f5ad68f3400"
integrity sha512-Fc68K0aTDrKIBvLnKTZ5Pf3MXK495YErrbHb1R6aTpfK5OdSFj0rVN7ib6Tx6ePrZ2gsjLqr0s98NG7l96KSQw==
dependencies:
"@typescript-eslint/typescript-estree" "6.7.2"
"@typescript-eslint/utils" "6.7.2"
"@typescript-eslint/typescript-estree" "6.7.3"
"@typescript-eslint/utils" "6.7.3"
debug "^4.3.4"
ts-api-utils "^1.0.1"

Expand All @@ -955,10 +955,10 @@
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.45.0.tgz#794760b9037ee4154c09549ef5a96599621109c5"
integrity sha512-QQij+u/vgskA66azc9dCmx+rev79PzX8uDHpsqSjEFtfF2gBUTRCpvYMh2gw2ghkJabNkPlSUCimsyBEQZd1DA==

"@typescript-eslint/types@6.7.2":
version "6.7.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.7.2.tgz#75a615a6dbeca09cafd102fe7f465da1d8a3c066"
integrity sha512-flJYwMYgnUNDAN9/GAI3l8+wTmvTYdv64fcH8aoJK76Y+1FCZ08RtI5zDerM/FYT5DMkAc+19E4aLmd5KqdFyg==
"@typescript-eslint/types@6.7.3":
version "6.7.3"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.7.3.tgz#0402b5628a63f24f2dc9d4a678e9a92cc50ea3e9"
integrity sha512-4g+de6roB2NFcfkZb439tigpAMnvEIg3rIjWQ+EM7IBaYt/CdJt6em9BJ4h4UpdgaBWdmx2iWsafHTrqmgIPNw==

"@typescript-eslint/typescript-estree@5.45.0":
version "5.45.0"
Expand All @@ -973,30 +973,30 @@
semver "^7.3.7"
tsutils "^3.21.0"

"@typescript-eslint/typescript-estree@6.7.2":
version "6.7.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.2.tgz#ce5883c23b581a5caf878af641e49dd0349238c7"
integrity sha512-kiJKVMLkoSciGyFU0TOY0fRxnp9qq1AzVOHNeN1+B9erKFCJ4Z8WdjAkKQPP+b1pWStGFqezMLltxO+308dJTQ==
"@typescript-eslint/typescript-estree@6.7.3":
version "6.7.3"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.3.tgz#ec5bb7ab4d3566818abaf0e4a8fa1958561b7279"
integrity sha512-YLQ3tJoS4VxLFYHTw21oe1/vIZPRqAO91z6Uv0Ss2BKm/Ag7/RVQBcXTGcXhgJMdA4U+HrKuY5gWlJlvoaKZ5g==
dependencies:
"@typescript-eslint/types" "6.7.2"
"@typescript-eslint/visitor-keys" "6.7.2"
"@typescript-eslint/types" "6.7.3"
"@typescript-eslint/visitor-keys" "6.7.3"
debug "^4.3.4"
globby "^11.1.0"
is-glob "^4.0.3"
semver "^7.5.4"
ts-api-utils "^1.0.1"

"@typescript-eslint/utils@6.7.2":
version "6.7.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.7.2.tgz#b9ef0da6f04932167a9222cb4ac59cb187165ebf"
integrity sha512-ZCcBJug/TS6fXRTsoTkgnsvyWSiXwMNiPzBUani7hDidBdj1779qwM1FIAmpH4lvlOZNF3EScsxxuGifjpLSWQ==
"@typescript-eslint/utils@6.7.3":
version "6.7.3"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.7.3.tgz#96c655816c373135b07282d67407cb577f62e143"
integrity sha512-vzLkVder21GpWRrmSR9JxGZ5+ibIUSudXlW52qeKpzUEQhRSmyZiVDDj3crAth7+5tmN1ulvgKaCU2f/bPRCzg==
dependencies:
"@eslint-community/eslint-utils" "^4.4.0"
"@types/json-schema" "^7.0.12"
"@types/semver" "^7.5.0"
"@typescript-eslint/scope-manager" "6.7.2"
"@typescript-eslint/types" "6.7.2"
"@typescript-eslint/typescript-estree" "6.7.2"
"@typescript-eslint/scope-manager" "6.7.3"
"@typescript-eslint/types" "6.7.3"
"@typescript-eslint/typescript-estree" "6.7.3"
semver "^7.5.4"

"@typescript-eslint/visitor-keys@5.45.0":
Expand All @@ -1007,12 +1007,12 @@
"@typescript-eslint/types" "5.45.0"
eslint-visitor-keys "^3.3.0"

"@typescript-eslint/visitor-keys@6.7.2":
version "6.7.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.2.tgz#4cb2bd786f1f459731b0ad1584c9f73e1c7a4d5c"
integrity sha512-uVw9VIMFBUTz8rIeaUT3fFe8xIUx8r4ywAdlQv1ifH+6acn/XF8Y6rwJ7XNmkNMDrTW+7+vxFFPIF40nJCVsMQ==
"@typescript-eslint/visitor-keys@6.7.3":
version "6.7.3"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.3.tgz#83809631ca12909bd2083558d2f93f5747deebb2"
integrity sha512-HEVXkU9IB+nk9o63CeICMHxFWbHWr3E1mpilIQBe9+7L/lH97rleFLVtYsfnWB+JVMaiFnEaxvknvmIzX+CqVg==
dependencies:
"@typescript-eslint/types" "6.7.2"
"@typescript-eslint/types" "6.7.3"
eslint-visitor-keys "^3.4.1"

abort-controller@^3.0.0:
Expand Down

0 comments on commit b6dd384

Please sign in to comment.