Skip to content

Commit b92ecfa

Browse files
authored
Implement retry logic for the DO cache to prevent when revalidating content. (#3098)
1 parent 416bde7 commit b92ecfa

File tree

4 files changed

+69
-15
lines changed

4 files changed

+69
-15
lines changed

.changeset/healthy-rocks-crash.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"gitbook": patch
3+
---
4+
5+
Implement retry logic for the DO cache to prevent when revalidating content.

bun.lock

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@
166166
"name": "@gitbook/icons",
167167
"version": "0.2.0",
168168
"bin": {
169-
"gitbook-icons": "./bin/gitbook-icons.js",
169+
"gitbook-icons": "./bin/gitbook-icons.js"
170170
},
171171
"dependencies": {
172172
"@fortawesome/fontawesome-free": "^6.6.0",
@@ -223,7 +223,7 @@
223223
"name": "@gitbook/react-math",
224224
"version": "0.6.0",
225225
"bin": {
226-
"gitbook-math": "./bin/gitbook-math.js",
226+
"gitbook-math": "./bin/gitbook-math.js"
227227
},
228228
"dependencies": {
229229
"object-hash": "^3.0.0",
@@ -4025,7 +4025,7 @@
40254025

40264026
"gaxios/node-fetch": ["[email protected]", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
40274027

4028-
"gitbook-v2/next": ["[email protected]", "", { "dependencies": { "@next/env": "15.3.0-canary.33", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.3.0-canary.33", "@next/swc-darwin-x64": "15.3.0-canary.33", "@next/swc-linux-arm64-gnu": "15.3.0-canary.33", "@next/swc-linux-arm64-musl": "15.3.0-canary.33", "@next/swc-linux-x64-gnu": "15.3.0-canary.33", "@next/swc-linux-x64-musl": "15.3.0-canary.33", "@next/swc-win32-arm64-msvc": "15.3.0-canary.33", "@next/swc-win32-x64-msvc": "15.3.0-canary.33", "sharp": "^0.33.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-5Yc/W1hqOgibDoxnLiOvKHGId76/F+SrvlbZSw0LHhsmWYat6qAEaxv28vlHxj9OiRBqtrp0Ydsb+6RmYjv6IA=="],
4028+
"gitbook-v2/next": ["[email protected]", "", { "dependencies": { "@next/env": "15.3.0-canary.37", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.3.0-canary.37", "@next/swc-darwin-x64": "15.3.0-canary.37", "@next/swc-linux-arm64-gnu": "15.3.0-canary.37", "@next/swc-linux-arm64-musl": "15.3.0-canary.37", "@next/swc-linux-x64-gnu": "15.3.0-canary.37", "@next/swc-linux-x64-musl": "15.3.0-canary.37", "@next/swc-win32-arm64-msvc": "15.3.0-canary.37", "@next/swc-win32-x64-msvc": "15.3.0-canary.37", "sharp": "^0.33.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-K9BJZoFYnp+WqR7jYSCzrBJHOwTXJOED964z0En7qszrjF2HXfa24W2bFAMBwK56JD+h4lUyhpbgBoDiDN+vnA=="],
40294029

40304030
"global-dirs/ini": ["[email protected]", "", {}, "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ=="],
40314031

@@ -4897,23 +4897,23 @@
48974897

48984898
"gaxios/https-proxy-agent/debug": ["[email protected]", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
48994899

4900-
"gitbook-v2/next/@next/env": ["@next/[email protected].33", "", {}, "sha512-yPuNYRSFLHtulgy5Mge7tEy2GT/SmIcC7ZM9mJRFVtCsjjR6hqqOkLdbu/6cl7qT0x5ADRfpokT5Rf5bTzUumA=="],
4900+
"gitbook-v2/next/@next/env": ["@next/[email protected].37", "", {}, "sha512-NoG8pEk34zpIphk7vSTZ6zs3rj33jtxWO5E2o+bCE8gjFQunChNnRWP1OPo2cATmnc64aJKcvdX5uOuEtAvoUw=="],
49014901

4902-
"gitbook-v2/next/@next/swc-darwin-arm64": ["@next/[email protected].33", "", { "os": "darwin", "cpu": "arm64" }, "sha512-oWIA9x2llzFxrvnz/6ZNmiMb5yrfR6WfRKa28mo+6c4e9r7M0gKFpNd5RbDZF+fF2RTetrv57ze+Cm9KkTUrSg=="],
4902+
"gitbook-v2/next/@next/swc-darwin-arm64": ["@next/[email protected].37", "", { "os": "darwin", "cpu": "arm64" }, "sha512-H+gorYP1jSJCmPDpTlQIyLS6GXkA6WZ74H7lxHeXWdAWyP7zv/MmxuhPmsD/RFzYEwZNDSk9jVFpYcOvfWK1Hw=="],
49034903

4904-
"gitbook-v2/next/@next/swc-darwin-x64": ["@next/[email protected].33", "", { "os": "darwin", "cpu": "x64" }, "sha512-Yj1S17ww6ga6VCCNbWrA0URQsuT3Xb5dE5hToo7OGf2NOFN9zEVMYbDp6CO4/ugOB4BslR8tzSVyfKyytibdCQ=="],
4904+
"gitbook-v2/next/@next/swc-darwin-x64": ["@next/[email protected].37", "", { "os": "darwin", "cpu": "x64" }, "sha512-zul0GsE7SGga8DdBWHRYzdQC5WST1RMmVJRxutvqCqcm7R/GosIzibrEFE5AkW7XMh6bZCO71r4zKPehTuWFcg=="],
49054905

4906-
"gitbook-v2/next/@next/swc-linux-arm64-gnu": ["@next/[email protected].33", "", { "os": "linux", "cpu": "arm64" }, "sha512-nkV4cw4w23qzpkgG9ITeLnXvLhV/i4ixy6NGJGCxOsYSoGXA5O6KD+ZEoYSSxRuM7rVVUfu6dO7A+W9s74U63Q=="],
4906+
"gitbook-v2/next/@next/swc-linux-arm64-gnu": ["@next/[email protected].37", "", { "os": "linux", "cpu": "arm64" }, "sha512-ucAOG0sMG2MkNbGTFAI0qncbkci0VE34xjwcpxUT7LUv/rWG83oVOcxcGY5xVbz9V6MpPCxr62FV3I5h6t+w0w=="],
49074907

4908-
"gitbook-v2/next/@next/swc-linux-arm64-musl": ["@next/[email protected].33", "", { "os": "linux", "cpu": "arm64" }, "sha512-Y/se0HzGrooecLp1JWWgODK89te2+akTr88NkYKGW63rGVrJvevXDZ8jJQiSMCwUq+OAWBRoSyrswLI+/9VMOQ=="],
4908+
"gitbook-v2/next/@next/swc-linux-arm64-musl": ["@next/[email protected].37", "", { "os": "linux", "cpu": "arm64" }, "sha512-hVnST5YroTumu1y+UB4HXN2mbmTs5VWqsr8KmQonAc7+Uu9hvjSb79cpCD5B97h0TBb34Z2vr90hN9w0Q06PFA=="],
49094909

4910-
"gitbook-v2/next/@next/swc-linux-x64-gnu": ["@next/[email protected].33", "", { "os": "linux", "cpu": "x64" }, "sha512-ENGvmcSyu51THeSG9i5vQmRVa6EriDmDVgnUGYm5o/X4lrU/xWNNMfpg9Sp0vjsyX3TCw6DSa5x+oZw8w4lPfw=="],
4910+
"gitbook-v2/next/@next/swc-linux-x64-gnu": ["@next/[email protected].37", "", { "os": "linux", "cpu": "x64" }, "sha512-p7ArUaiTRm5pSqHg60UtJbZUzsOU90idibVRPgnXd+0Kec7n6TClE3pWcIFow9by0bpSi6kmbNmkHf61i0Kerw=="],
49114911

4912-
"gitbook-v2/next/@next/swc-linux-x64-musl": ["@next/[email protected].33", "", { "os": "linux", "cpu": "x64" }, "sha512-3UEL+tjNYKL/OG1hlR69C6S5voe9CXNX60O9Irl7TAZAxozdN+J3PrsfZ07QRduQIze0VORWpAh90LNnqXp1FQ=="],
4912+
"gitbook-v2/next/@next/swc-linux-x64-musl": ["@next/[email protected].37", "", { "os": "linux", "cpu": "x64" }, "sha512-Egi3swoN3pbseJ3oRGFYs94ICEjmzmY+exWfNlpycjBMWp1m/QzIcIxHTdz2/Lqe5SNGVMrJ8uEDNAD2knqgvw=="],
49134913

4914-
"gitbook-v2/next/@next/swc-win32-arm64-msvc": ["@next/[email protected].33", "", { "os": "win32", "cpu": "arm64" }, "sha512-FQHFbBi340K4uAR/M7FbzHMnucXsPyuns2PKMy9PLaZKgXiBDkVAurI4FRlIKspG35vddnqybn4K/1BMqW4mWg=="],
4914+
"gitbook-v2/next/@next/swc-win32-arm64-msvc": ["@next/[email protected].37", "", { "os": "win32", "cpu": "arm64" }, "sha512-gOE0lBeS/ztrSHnXimEp9D5OOTN86moSfMgC05ei/UJSFyT7YEZa4FRYRDzctNEilRJC2DBnxjEpeD+JhDuf6A=="],
49154915

4916-
"gitbook-v2/next/@next/swc-win32-x64-msvc": ["@next/[email protected].33", "", { "os": "win32", "cpu": "x64" }, "sha512-sctiGfzVmVMw+7995NzH5UM+kQr7bz0AJN07pVaubiT9ByRhcsp7WLnyWEVs2NYQWzVpHEfNwHs5PbZ4w0C3FQ=="],
4916+
"gitbook-v2/next/@next/swc-win32-x64-msvc": ["@next/[email protected].37", "", { "os": "win32", "cpu": "x64" }, "sha512-J/SjJYN1Nj/olRC0ykfDNKKoxJss4a3Zf340UeyHbmbqeKHvGbT/H0QFMx+3DT+Sqar3PqbyZBLbxpE/QYct1w=="],
49174917

49184918
"gitbook-v2/next/postcss": ["[email protected]", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
49194919

packages/gitbook/src/app/(global)/~gitbook/revalidate/route.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,7 @@ export async function POST(req: NextRequest) {
4242
} catch (err: unknown) {
4343
return NextResponse.json(
4444
{
45-
error: `unexpected error ${err}`,
46-
stack: err instanceof Error ? err.stack : null,
45+
error: `${err}`,
4746
},
4847
{ status: 500 }
4948
);

packages/gitbook/src/lib/cache/cloudflare-do.ts

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,10 @@ export const cloudflareDOCache: CacheBackend = {
6464
return;
6565
}
6666

67-
const keys = await stub.purge();
67+
const keys = await retryOnDurableObjectError(async () => {
68+
return await stub.purge();
69+
});
70+
6871
keys.forEach((key) => {
6972
entries.push({ key, tag });
7073
});
@@ -99,3 +102,50 @@ async function getStub(tag: string): Promise<CacheObjectStub | null> {
99102

100103
return stub;
101104
}
105+
106+
/**
107+
* Retry an operation on a Durable Object if it fails with a retriable error.
108+
* It will retry up to 4 times with an exponential backoff.
109+
*/
110+
export async function retryOnDurableObjectError<T>(
111+
operation: () => T | Promise<T>,
112+
attemptsLeft = 4,
113+
delay = 50
114+
): Promise<T> {
115+
if (attemptsLeft <= 0) {
116+
return operation();
117+
}
118+
119+
try {
120+
return await operation();
121+
} catch (error) {
122+
if (!shouldRetryError(error)) {
123+
throw error;
124+
}
125+
126+
if (attemptsLeft > 0) {
127+
await new Promise((resolve) => setTimeout(resolve, delay));
128+
return retryOnDurableObjectError(operation, attemptsLeft - 1, delay * 2);
129+
}
130+
131+
throw error;
132+
}
133+
}
134+
135+
const RETRIABLE_ERROR_MESSAGES = new Set([
136+
'Cannot resolve Durable Object due to transient issue on remote node.',
137+
'internal error',
138+
`Durable Object's isolate exceeded its memory limit and was reset.`,
139+
'cannot access storage because object has moved to a different machine',
140+
'Durable Object reset because its code was updated.',
141+
"The Durable Object's code has been updated, this version can no longer access storage.",
142+
// https://developers.cloudflare.com/workers/observability/errors/#runtime-errors
143+
'Network connection lost.',
144+
]);
145+
146+
/**
147+
* Check if an error should be retried based on its error message.
148+
*/
149+
function shouldRetryError(error: unknown): boolean {
150+
return error instanceof Error && RETRIABLE_ERROR_MESSAGES.has(error.message);
151+
}

0 commit comments

Comments
 (0)