From 8dd3214a8adc5db151046f8eed667f4c3904ef6f Mon Sep 17 00:00:00 2001 From: Fabian Koder Date: Tue, 28 Oct 2025 15:48:16 +0100 Subject: [PATCH 1/3] Add p-limit package --- .../nextjs-cache-handler/package-lock.json | 202 ++++++++++++------ packages/nextjs-cache-handler/package.json | 3 +- 2 files changed, 144 insertions(+), 61 deletions(-) diff --git a/packages/nextjs-cache-handler/package-lock.json b/packages/nextjs-cache-handler/package-lock.json index 483d406..5acdeb3 100644 --- a/packages/nextjs-cache-handler/package-lock.json +++ b/packages/nextjs-cache-handler/package-lock.json @@ -10,7 +10,8 @@ "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2", - "lru-cache": "11.2.2" + "lru-cache": "11.2.2", + "p-limit": "^7.2.0" }, "devDependencies": { "@eslint/js": "^9.38.0", @@ -85,6 +86,7 @@ "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -1164,7 +1166,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -1187,7 +1188,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -1210,7 +1210,6 @@ "os": [ "darwin" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -1227,7 +1226,6 @@ "os": [ "darwin" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -1244,7 +1242,6 @@ "os": [ "linux" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -1261,7 +1258,6 @@ "os": [ "linux" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -1278,7 +1274,6 @@ "os": [ "linux" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -1295,7 +1290,6 @@ "os": [ "linux" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -1312,7 +1306,6 @@ "os": [ "linux" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -1329,7 +1322,6 @@ "os": [ "linux" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -1346,7 +1338,6 @@ "os": [ "linux" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -1363,7 +1354,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -1386,7 +1376,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -1409,7 +1398,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -1432,7 +1420,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -1455,7 +1442,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -1478,7 +1464,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -1501,7 +1486,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -1521,7 +1505,6 @@ ], "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, - "peer": true, "dependencies": { "@emnapi/runtime": "^1.4.4" }, @@ -1544,7 +1527,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -1564,7 +1546,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -1584,7 +1565,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -2201,8 +2181,7 @@ "version": "15.5.6", "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.6.tgz", "integrity": "sha512-3qBGRW+sCGzgbpc5TS1a0p7eNxnOarGVQhZxfvTdnV0gFI61lX7QNtQ4V1TSREctXzYn5NetbUsLvyqwLFJM6Q==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@next/swc-darwin-arm64": { "version": "15.5.6", @@ -2216,7 +2195,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 10" } @@ -2233,7 +2211,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 10" } @@ -2250,7 +2227,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10" } @@ -2267,7 +2243,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10" } @@ -2284,7 +2259,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10" } @@ -2301,7 +2275,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10" } @@ -2318,7 +2291,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 10" } @@ -2335,7 +2307,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 10" } @@ -2722,7 +2693,6 @@ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.8.0" } @@ -2908,6 +2878,7 @@ "integrity": "sha512-6JSSaBZmsKvEkbRUkf7Zj7dru/8ZCrJxAqArcLaVMee5907JdtEbKGsZ7zNiIm/UAkpGUkaSMZEXShnN2D1HZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.1", "@typescript-eslint/types": "8.46.1", @@ -3415,6 +3386,7 @@ "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3687,6 +3659,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001726", "electron-to-chromium": "^1.5.173", @@ -3863,8 +3836,7 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/cliui": { "version": "8.0.1", @@ -3953,7 +3925,6 @@ "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" @@ -3986,7 +3957,6 @@ "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" @@ -4111,7 +4081,6 @@ "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", "license": "Apache-2.0", "optional": true, - "peer": true, "engines": { "node": ">=8" } @@ -4197,6 +4166,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -4258,6 +4228,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "dev": true, + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -5016,8 +4987,7 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/is-extglob": { "version": "2.1.1", @@ -5204,6 +5174,7 @@ "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "30.2.0", "@jest/types": "30.2.0", @@ -5240,6 +5211,35 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, + "node_modules/jest-changed-files/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-changed-files/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/jest-circus": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.2.0.tgz", @@ -5272,6 +5272,35 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, + "node_modules/jest-circus/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-circus/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/jest-cli": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.2.0.tgz", @@ -5619,6 +5648,35 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, + "node_modules/jest-runner/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-runner/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/jest-runtime": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.2.0.tgz", @@ -5963,7 +6021,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "peer": true, "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -6147,7 +6204,6 @@ } ], "license": "MIT", - "peer": true, "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -6326,15 +6382,15 @@ } }, "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-7.2.0.tgz", + "integrity": "sha512-ATHLtwoTNDloHRFFxFJdHnG6n2WUeFjaR8XQMFdKIv0xkXjrER8/iG9iu265jOM95zXHAfv9oTkqhrfbIzosrQ==", + "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" + "yocto-queue": "^1.2.1" }, "engines": { - "node": ">=10" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -6355,6 +6411,35 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -6587,7 +6672,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", @@ -6654,6 +6738,7 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -7043,7 +7128,6 @@ "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" } @@ -7068,7 +7152,6 @@ "hasInstallScript": true, "license": "Apache-2.0", "optional": true, - "peer": true, "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.4", @@ -7144,7 +7227,6 @@ "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "is-arrayish": "^0.3.1" } @@ -7204,7 +7286,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -7401,7 +7482,6 @@ "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", "license": "MIT", - "peer": true, "dependencies": { "client-only": "0.0.1" }, @@ -7754,6 +7834,7 @@ "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" @@ -7808,6 +7889,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8199,12 +8281,12 @@ } }, "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz", + "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=12.20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" diff --git a/packages/nextjs-cache-handler/package.json b/packages/nextjs-cache-handler/package.json index cec38bc..d44333f 100644 --- a/packages/nextjs-cache-handler/package.json +++ b/packages/nextjs-cache-handler/package.json @@ -89,7 +89,8 @@ }, "dependencies": { "cluster-key-slot": "1.1.2", - "lru-cache": "11.2.2" + "lru-cache": "11.2.2", + "p-limit": "^7.2.0" }, "devDependencies": { "@eslint/js": "^9.38.0", From 0ac2becf35fbdfcbb90d1f6fbfc9b6b01231c42d Mon Sep 17 00:00:00 2001 From: Fabian Koder Date: Tue, 28 Oct 2025 15:51:57 +0100 Subject: [PATCH 2/3] Populate page-cache in parallel instead of sequentially Previously, all routes were set one-by-one. Instead, up to `availableParallelism` pages are now concurrently read from the filesystem and populated into the cache handler. --- .../instrumentation/register-initial-cache.ts | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/packages/nextjs-cache-handler/src/instrumentation/register-initial-cache.ts b/packages/nextjs-cache-handler/src/instrumentation/register-initial-cache.ts index da2b6c4..693a563 100644 --- a/packages/nextjs-cache-handler/src/instrumentation/register-initial-cache.ts +++ b/packages/nextjs-cache-handler/src/instrumentation/register-initial-cache.ts @@ -1,5 +1,6 @@ import { promises as fsPromises } from "node:fs"; import path from "node:path"; +import os from "node:os"; import { PRERENDER_MANIFEST, SERVER_DIRECTORY } from "next/constants"; import type { PrerenderManifest } from "next/dist/build"; import { CACHE_ONE_YEAR } from "next/dist/lib/constants"; @@ -12,6 +13,7 @@ import { import type { OutgoingHttpHeaders } from "http"; import { getTagsFromHeaders } from "../helpers/getTagsFromHeaders"; import { Revalidate } from "../handlers/cache-handler.types"; +import pLimit from "p-limit"; type CacheHandlerType = typeof import("../handlers/cache-handler").CacheHandler; @@ -386,18 +388,22 @@ export async function registerInitialCache( } } - for (const [ - cachePath, - { dataRoute, initialRevalidateSeconds }, - ] of Object.entries(prerenderManifest.routes)) { - if (populatePages && dataRoute?.endsWith(".json")) { - await setPageCache(cachePath, "pages", initialRevalidateSeconds); - } else if (populatePages && dataRoute?.endsWith(".rsc")) { - await setPageCache(cachePath, "app", initialRevalidateSeconds); - } else if (populateRoutes && dataRoute === null) { - await setRouteCache(cachePath, "app", initialRevalidateSeconds); - } - } + const limit = pLimit(os.availableParallelism()); + + const promises = Object.entries(prerenderManifest.routes).map( + ([cachePath, { dataRoute, initialRevalidateSeconds }]) => + limit(async () => { + if (populatePages && dataRoute?.endsWith(".json")) { + await setPageCache(cachePath, "pages", initialRevalidateSeconds); + } else if (populatePages && dataRoute?.endsWith(".rsc")) { + await setPageCache(cachePath, "app", initialRevalidateSeconds); + } else if (populateRoutes && dataRoute === null) { + await setRouteCache(cachePath, "app", initialRevalidateSeconds); + } + }), + ); + + await Promise.all(promises); if (!populateFetch) { return; From d4cf990bd14fcbaeedd67084bc90867903f39ca4 Mon Sep 17 00:00:00 2001 From: Fabian Koder Date: Tue, 28 Oct 2025 16:40:08 +0100 Subject: [PATCH 3/3] Introduced a promise-backed latch so cache setup runs once before potential parallel work Added `#configureTask` alongside the existing merged handler state and wired a new `#ensureConfigured()` helper that guards the original configuration routine. The `get`, `set`, and `revalidateTag` entry points now await `#ensureConfigured()`, so they can be executed in parallel after the one-time initialization completes without racing the user-provided `onCreation` hook --- .../src/handlers/cache-handler.ts | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/packages/nextjs-cache-handler/src/handlers/cache-handler.ts b/packages/nextjs-cache-handler/src/handlers/cache-handler.ts index 76b1497..e383ce0 100644 --- a/packages/nextjs-cache-handler/src/handlers/cache-handler.ts +++ b/packages/nextjs-cache-handler/src/handlers/cache-handler.ts @@ -126,6 +126,8 @@ export class CacheHandler implements NextCacheHandler { static #mergedHandler: Omit; + static #configureTask: Promise | undefined; + static #cacheListLength: number; static #debug = typeof process.env.NEXT_PRIVATE_DEBUG_CACHE !== "undefined"; @@ -381,13 +383,13 @@ export class CacheHandler implements NextCacheHandler { * * @param onCreationHook - The {@link OnCreationHook} function to be called during cache creation. * - + */ static onCreation(onCreationHook: OnCreationHook): void { CacheHandler.#onCreationHook = onCreationHook; } - static async #configureCacheHandler(): Promise { + static async #ensureConfigured(): Promise { if (CacheHandler.#mergedHandler) { if (CacheHandler.#debug) { console.info( @@ -398,6 +400,24 @@ export class CacheHandler implements NextCacheHandler { return; } + if (!CacheHandler.#configureTask) { + CacheHandler.#configureTask = (async () => { + try { + await CacheHandler.#configureCacheHandlerInternal(); + } finally { + CacheHandler.#configureTask = undefined; + } + })(); + } + + await CacheHandler.#configureTask; + } + + static async #configureCacheHandlerInternal(): Promise { + if (CacheHandler.#mergedHandler) { + return; + } + if (CacheHandler.#debug) { console.info( "[CacheHandler] %s", @@ -651,7 +671,7 @@ export class CacheHandler implements NextCacheHandler { | (GetIncrementalResponseCacheContext & { softTags?: null | [] }) | { softTags: [] } = { softTags: [] }, ): Promise { - await CacheHandler.#configureCacheHandler(); + await CacheHandler.#ensureConfigured(); const { softTags = [] } = ctx; @@ -699,7 +719,7 @@ export class CacheHandler implements NextCacheHandler { revalidate?: Revalidate; }, ): Promise { - await CacheHandler.#configureCacheHandler(); + await CacheHandler.#ensureConfigured(); if (CacheHandler.#debug) { console.info( @@ -776,7 +796,7 @@ export class CacheHandler implements NextCacheHandler { async revalidateTag( tag: CacheHandlerParametersRevalidateTag[0], ): Promise { - await CacheHandler.#configureCacheHandler(); + await CacheHandler.#ensureConfigured(); const tags = typeof tag === "string" ? [tag] : tag;