From 51dc7e6da50e14772c96eec90608e81756e3f514 Mon Sep 17 00:00:00 2001 From: Jacob Ebey Date: Tue, 2 May 2023 16:48:10 -0700 Subject: [PATCH 1/4] feat: retry revalidation caused by HDR --- packages/remix-react/browser.tsx | 2 ++ packages/remix-react/data.ts | 23 ++++++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/remix-react/browser.tsx b/packages/remix-react/browser.tsx index 4453d171d58..97d1b67c123 100644 --- a/packages/remix-react/browser.tsx +++ b/packages/remix-react/browser.tsx @@ -30,6 +30,7 @@ declare global { }; var __remixRouteModules: RouteModules; var __remixManifest: EntryContext["manifest"]; + var __remixRevalidation: number | undefined; var $RefreshRuntime$: { performReactRefresh: () => void; }; @@ -139,6 +140,7 @@ if (import.meta && import.meta.hot) { }, 1); } }); + window.__remixRevalidation = (window.__remixRevalidation || 0) + 1; router.revalidate(); } ); diff --git a/packages/remix-react/data.ts b/packages/remix-react/data.ts index 4991ec8720d..50f871b8cff 100644 --- a/packages/remix-react/data.ts +++ b/packages/remix-react/data.ts @@ -38,6 +38,7 @@ export function isDeferredResponse(response: any): boolean { ); } +let revalidationTimeout = 0; export async function fetchData( request: Request, routeId: string @@ -59,7 +60,27 @@ export async function fetchData( : await request.formData(); } - let response = await fetch(url.href, init); + let revalidation = window.__remixRevalidation; + if (typeof revalidation === "number") { + await new Promise((resolve) => setTimeout(resolve, revalidationTimeout)); + } + + let start = Date.now(); + let response = await fetch(url.href, init).catch((error) => { + if ( + typeof revalidation === "number" && + revalidation === window.__remixRevalidation && + error?.name === "TypeError" + ) { + // TODO: put just a little thought into this and make it adjust up and down + revalidationTimeout = + (revalidationTimeout + 10 * (Date.now() - start)) / 2; + revalidationTimeout = + revalidationTimeout > 1000 ? 1000 : revalidationTimeout; + return fetch(url.href, init); + } + throw error; + }); if (isErrorResponse(response)) { let data = await response.json(); From 20e7654cd0f461928638fa4b81f9a705828d161a Mon Sep 17 00:00:00 2001 From: Jacob Ebey Date: Tue, 30 May 2023 10:34:22 -0700 Subject: [PATCH 2/4] feat: implement a simple retry / backoff for HDR revalidation --- packages/remix-react/data.ts | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/packages/remix-react/data.ts b/packages/remix-react/data.ts index 50f871b8cff..a21538481ba 100644 --- a/packages/remix-react/data.ts +++ b/packages/remix-react/data.ts @@ -17,7 +17,7 @@ export function isCatchResponse(response: any): boolean { ); } -export function isErrorResponse(response: any): boolean { +export function isErrorResponse(response: any): response is Response { return ( response instanceof Response && response.headers.get("X-Remix-Error") != null @@ -38,10 +38,10 @@ export function isDeferredResponse(response: any): boolean { ); } -let revalidationTimeout = 0; export async function fetchData( request: Request, - routeId: string + routeId: string, + retry = 0 ): Promise { let url = new URL(request.url); url.searchParams.set("_data", routeId); @@ -60,24 +60,21 @@ export async function fetchData( : await request.formData(); } - let revalidation = window.__remixRevalidation; - if (typeof revalidation === "number") { - await new Promise((resolve) => setTimeout(resolve, revalidationTimeout)); + if (retry > 0) { + // Retry up to seven times waiting 20, 40, 80, 160, 320, 640, and 1280 ms + // between retries for a total of 2540 ms before giving up. + await new Promise((resolve) => setTimeout(resolve, 2 ** retry * 10)); } - let start = Date.now(); + let revalidation = window.__remixRevalidation; let response = await fetch(url.href, init).catch((error) => { if ( typeof revalidation === "number" && revalidation === window.__remixRevalidation && - error?.name === "TypeError" + error?.name === "TypeError" && + retry <= 7 ) { - // TODO: put just a little thought into this and make it adjust up and down - revalidationTimeout = - (revalidationTimeout + 10 * (Date.now() - start)) / 2; - revalidationTimeout = - revalidationTimeout > 1000 ? 1000 : revalidationTimeout; - return fetch(url.href, init); + return fetchData(request, routeId, retry + 1); } throw error; }); From 16e9a9ffc2202664f76270d0c54b80d814c9fa74 Mon Sep 17 00:00:00 2001 From: Jacob Ebey Date: Thu, 1 Jun 2023 12:46:34 -0700 Subject: [PATCH 3/4] update retry timings --- packages/remix-react/data.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/remix-react/data.ts b/packages/remix-react/data.ts index a21538481ba..892d31f37db 100644 --- a/packages/remix-react/data.ts +++ b/packages/remix-react/data.ts @@ -61,9 +61,9 @@ export async function fetchData( } if (retry > 0) { - // Retry up to seven times waiting 20, 40, 80, 160, 320, 640, and 1280 ms - // between retries for a total of 2540 ms before giving up. - await new Promise((resolve) => setTimeout(resolve, 2 ** retry * 10)); + // Retry up to 3 times waiting 50, 250, 1250 ms + // between retries for a total of 1550 ms before giving up. + await new Promise((resolve) => setTimeout(resolve, 5 ** retry * 10)); } let revalidation = window.__remixRevalidation; @@ -72,7 +72,7 @@ export async function fetchData( typeof revalidation === "number" && revalidation === window.__remixRevalidation && error?.name === "TypeError" && - retry <= 7 + retry < 3 ) { return fetchData(request, routeId, retry + 1); } From 9b840a400cab6c3dad14494b23b3e9def954e578 Mon Sep 17 00:00:00 2001 From: Jacob Ebey Date: Thu, 1 Jun 2023 12:47:36 -0700 Subject: [PATCH 4/4] changeset --- .changeset/hdr-revalidation-retry.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/hdr-revalidation-retry.md diff --git a/.changeset/hdr-revalidation-retry.md b/.changeset/hdr-revalidation-retry.md new file mode 100644 index 00000000000..949fb91bcf1 --- /dev/null +++ b/.changeset/hdr-revalidation-retry.md @@ -0,0 +1,5 @@ +--- +"@remix-run/react": patch +--- + +retry HDR revalidations in development mode to aid in 3rd party server race conditions