Skip to content

Commit a783c2d

Browse files
authored
Only retry API failures (#7831)
* Only retry API failures * fix lint
1 parent 26fa9e8 commit a783c2d

File tree

6 files changed

+44
-11
lines changed

6 files changed

+44
-11
lines changed

packages/wrangler/src/__tests__/versions/versions.upload.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,5 +169,7 @@ describe("versions upload", () => {
169169
Uploaded test-name (TIMINGS)
170170
Worker Version ID: 51e4886e-2db7-4900-8d38-fbfecfeab993"
171171
`);
172+
173+
expect(std.info).toContain("Retrying API call after error...");
172174
});
173175
});

packages/wrangler/src/deploy/deploy.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ import {
4949
} from "../sourcemap";
5050
import triggersDeploy from "../triggers/deploy";
5151
import { printBindings } from "../utils/print-bindings";
52-
import { retryOnError } from "../utils/retry";
52+
import { retryOnAPIFailure } from "../utils/retry";
5353
import {
5454
createDeployment,
5555
patchNonVersionedScriptSettings,
@@ -826,7 +826,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
826826
// If we're using the new APIs, first upload the version
827827
if (canUseNewVersionsDeploymentsApi) {
828828
// Upload new version
829-
const versionResult = await retryOnError(async () =>
829+
const versionResult = await retryOnAPIFailure(async () =>
830830
fetchResult<ApiVersion>(
831831
`/accounts/${accountId}/workers/scripts/${scriptName}/versions`,
832832
{
@@ -861,7 +861,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
861861
startup_time_ms: versionResult.startup_time_ms,
862862
};
863863
} else {
864-
result = await retryOnError(async () =>
864+
result = await retryOnAPIFailure(async () =>
865865
fetchResult<{
866866
id: string | null;
867867
etag: string | null;

packages/wrangler/src/parse.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ export class APIError extends ParseError {
8888
return false;
8989
}
9090

91+
isRetryable() {
92+
return String(this.#status).startsWith("5");
93+
}
94+
9195
// Allow `APIError`s to be marked as handled.
9296
#reportable = true;
9397
get reportable() {

packages/wrangler/src/pipelines/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { logger } from "../logger";
55
import * as metrics from "../metrics";
66
import { APIError } from "../parse";
77
import { requireAuth } from "../user";
8-
import { retryOnError } from "../utils/retry";
8+
import { retryOnAPIFailure } from "../utils/retry";
99
import { printWranglerBanner } from "../wrangler-banner";
1010
import {
1111
createPipeline,
@@ -63,7 +63,7 @@ async function authorizeR2Bucket(
6363

6464
// Wait for token to settle/propagate, retry up to 10 times, with 1s waits in-between errors
6565
!__testSkipDelaysFlag &&
66-
(await retryOnError(
66+
(await retryOnAPIFailure(
6767
async () => {
6868
await r2.send(
6969
new HeadBucketCommand({
Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,45 @@
11
import { setTimeout } from "node:timers/promises";
2+
import chalk from "chalk";
3+
import { logger } from "../logger";
4+
import { APIError } from "../parse";
25

3-
export async function retryOnError<T>(
6+
const MAX_ATTEMPTS = 3;
7+
/**
8+
* Wrap around calls to the Cloudflare API to automatically retry
9+
* calls that result in a 5xx error code, indicating an API failure.
10+
*
11+
* Retries will back off at a rate of 1000ms per retry, with a 0ms delay for the first retry
12+
*
13+
* Note: this will not retry 4xx or other failures, as those are
14+
* likely legitimate user error.
15+
*/
16+
export async function retryOnAPIFailure<T>(
417
action: () => T | Promise<T>,
5-
backoff = 2_000,
6-
attempts = 3
18+
backoff = 0,
19+
attempts = MAX_ATTEMPTS
720
): Promise<T> {
821
try {
922
return await action();
1023
} catch (err) {
24+
if (
25+
(err instanceof APIError && !err.isRetryable()) ||
26+
!(err instanceof TypeError)
27+
) {
28+
throw err;
29+
}
30+
31+
logger.info(chalk.dim(`Retrying API call after error...`));
32+
logger.debug(err);
33+
1134
if (attempts <= 1) {
1235
throw err;
1336
}
1437

1538
await setTimeout(backoff);
16-
return retryOnError(action, backoff, attempts - 1);
39+
return retryOnAPIFailure(
40+
action,
41+
backoff + (MAX_ATTEMPTS - attempts) * 1000,
42+
attempts - 1
43+
);
1744
}
1845
}

packages/wrangler/src/versions/upload.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ import { getRules } from "../utils/getRules";
5353
import { getScriptName } from "../utils/getScriptName";
5454
import { isLegacyEnv } from "../utils/isLegacyEnv";
5555
import { printBindings } from "../utils/print-bindings";
56-
import { retryOnError } from "../utils/retry";
56+
import { retryOnAPIFailure } from "../utils/retry";
5757
import type { AssetsOptions } from "../assets";
5858
import type { Config } from "../config";
5959
import type { Rule } from "../config/environment";
@@ -741,7 +741,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
741741
try {
742742
const body = createWorkerUploadForm(worker);
743743

744-
const result = await retryOnError(async () =>
744+
const result = await retryOnAPIFailure(async () =>
745745
fetchResult<{
746746
id: string;
747747
startup_time_ms: number;

0 commit comments

Comments
 (0)