From f115d13631d2f4ad1d9154e20d7751069ef48b3b Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Fri, 5 Mar 2021 01:50:05 +0800 Subject: [PATCH 1/4] dedupe in-flight getServerSideData requests --- packages/next/next-server/lib/router/router.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/next/next-server/lib/router/router.ts b/packages/next/next-server/lib/router/router.ts index fbe4487f9fe08..28b1e7d8ab212 100644 --- a/packages/next/next-server/lib/router/router.ts +++ b/packages/next/next-server/lib/router/router.ts @@ -503,6 +503,9 @@ export default class Router implements BaseRouter { components: { [pathname: string]: PrivateRouteInfo } // Static Data Cache sdc: { [asPath: string]: object } = {} + // In-flight Server Data Requests, for deduping + sdr: { [asPath: string]: Promise } = {} + sub: Subscription clc: ComponentLoadCancel pageLoader: any @@ -1598,7 +1601,19 @@ export default class Router implements BaseRouter { } _getServerData(dataHref: string): Promise { - return fetchNextData(dataHref, this.isSsr) + const { href: resourceKey } = new URL(dataHref, window.location.href) + if (this.sdr[resourceKey]) { + return this.sdr[resourceKey] + } + return (this.sdr[resourceKey] = fetchNextData(dataHref, this.isSsr) + .then((data) => { + delete this.sdr[resourceKey] + return data + }) + .catch((err) => { + delete this.sdr[resourceKey] + throw err + })) } getInitialProps( From 00ab1bfbe11171fafa6ccadbb3744d31471d8a5a Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Fri, 5 Mar 2021 02:18:08 +0800 Subject: [PATCH 2/4] add test --- .../getserversideprops/pages/index.js | 4 +++ .../getserversideprops/pages/slow/index.js | 26 +++++++++++++++++++ .../getserversideprops/test/index.test.js | 26 +++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 test/integration/getserversideprops/pages/slow/index.js diff --git a/test/integration/getserversideprops/pages/index.js b/test/integration/getserversideprops/pages/index.js index 223f68b050f65..f940f57fa4b72 100644 --- a/test/integration/getserversideprops/pages/index.js +++ b/test/integration/getserversideprops/pages/index.js @@ -37,6 +37,10 @@ const Page = ({ world, time, url }) => { to normal
+ + to slow + +
to dynamic diff --git a/test/integration/getserversideprops/pages/slow/index.js b/test/integration/getserversideprops/pages/slow/index.js new file mode 100644 index 0000000000000..2e48d1ded6f41 --- /dev/null +++ b/test/integration/getserversideprops/pages/slow/index.js @@ -0,0 +1,26 @@ +import Link from 'next/link' + +let serverHitCount = 0 + +export async function getServerSideProps() { + await new Promise((resolve) => setTimeout(resolve, 500)) + return { + props: { + count: ++serverHitCount, + }, + } +} + +export default ({ count }) => ( + <> +

a slow page

+

hit: {count}

+ + to home + +
+ + to something + + +) diff --git a/test/integration/getserversideprops/test/index.test.js b/test/integration/getserversideprops/test/index.test.js index 96524ee813aca..ed18f0639ffbe 100644 --- a/test/integration/getserversideprops/test/index.test.js +++ b/test/integration/getserversideprops/test/index.test.js @@ -616,6 +616,32 @@ const runTests = (dev = false) => { expect(curRandom).toBe(initialRandom + '') }) + it('should dedupe server data requests', async () => { + const browser = await webdriver(appPort, '/') + await waitFor(2000) + + // Keep clicking on the link + await browser.elementByCss('#slow').click() + await browser.elementByCss('#slow').click() + await browser.elementByCss('#slow').click() + await browser.elementByCss('#slow').click() + + await check(() => getBrowserBodyText(browser), /a slow page/) + + // Requests should be deduped + const hitCount = await browser.elementByCss('#hit').text() + expect(hitCount).toBe('hit: 1') + + // Should send request again + await browser.elementByCss('#home').click() + await browser.waitForElementByCss('#slow') + await browser.elementByCss('#slow').click() + await check(() => getBrowserBodyText(browser), /a slow page/) + + const newHitCount = await browser.elementByCss('#hit').text() + expect(newHitCount).toBe('hit: 2') + }) + if (dev) { it('should not show warning from url prop being returned', async () => { const urlPropPage = join(appDir, 'pages/url-prop.js') From 1b4666484ac6652d8b8f5124350ca0c1b7c2ce71 Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Fri, 5 Mar 2021 12:53:48 +0800 Subject: [PATCH 3/4] fix test --- test/integration/getserversideprops/test/index.test.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/integration/getserversideprops/test/index.test.js b/test/integration/getserversideprops/test/index.test.js index ed18f0639ffbe..dee20cb3e6272 100644 --- a/test/integration/getserversideprops/test/index.test.js +++ b/test/integration/getserversideprops/test/index.test.js @@ -142,6 +142,12 @@ const expectedManifestRoutes = () => [ ), page: '/refresh', }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/slow.json$` + ), + page: '/slow', + }, { dataRouteRegex: normalizeRegEx( `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/something.json$` From 8e26c3e3be8682d835e6a757192f0fba1f91adf4 Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Fri, 5 Mar 2021 17:27:26 +0800 Subject: [PATCH 4/4] update test --- test/integration/build-output/test/index.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/build-output/test/index.test.js b/test/integration/build-output/test/index.test.js index 5c592ebee4487..433a9af5811d8 100644 --- a/test/integration/build-output/test/index.test.js +++ b/test/integration/build-output/test/index.test.js @@ -104,7 +104,7 @@ describe('Build Output', () => { expect(parseFloat(err404FirstLoad)).toBeCloseTo(67.1, 0) expect(err404FirstLoad.endsWith('kB')).toBe(true) - expect(parseFloat(sharedByAll)).toBeCloseTo(63.8, 1) + expect(parseFloat(sharedByAll)).toBeCloseTo(63.9, 1) expect(sharedByAll.endsWith('kB')).toBe(true) if (_appSize.endsWith('kB')) {