From 522417c9b6dee43dbb873c7f7a2a16ed77d7a511 Mon Sep 17 00:00:00 2001 From: Mark Knichel <7355009+mknichel@users.noreply.github.com> Date: Wed, 3 Jan 2024 10:14:37 -0800 Subject: [PATCH 1/5] Add experimental options for more parallelization in webpack builds --- errors/parallel-build-without-worker.mdx | 20 +++++ packages/next/src/build/index.ts | 80 +++++++++++++------ packages/next/src/server/config-schema.ts | 2 + packages/next/src/server/config-shared.ts | 31 +++++++ test/e2e/app-dir/app/next.config.js | 2 + .../good-routes/next.config.js | 1 + 6 files changed, 110 insertions(+), 26 deletions(-) create mode 100644 errors/parallel-build-without-worker.mdx diff --git a/errors/parallel-build-without-worker.mdx b/errors/parallel-build-without-worker.mdx new file mode 100644 index 0000000000000..bac156ab7e467 --- /dev/null +++ b/errors/parallel-build-without-worker.mdx @@ -0,0 +1,20 @@ +--- +title: Parallel Build Without Build Worker +--- + +## Why This Error Occurred + +The `experimental.parallelServerAndEdgeCompiles` and `experimental.parallelServerBuildTraces` +options require that the `experimental.webpackBuildWorker` option is set to true. These +options use workers to improve the paralellization of the build which may improve performance, +but the build may use more memory at the same time. + +## Possible Ways to Fix It + +Build workers are enabled by default unless you have a custom webpack config. +You can force enable the option by setting `config.experimental.webpackBuildWorker: true` in the config, +but some webpack configurations may not be compatible. + +## Useful Links + +Also see https://nextjs.org/docs/messages/webpack-build-worker-opt-out diff --git a/packages/next/src/build/index.ts b/packages/next/src/build/index.ts index 1f667e173845e..6d31114decc5a 100644 --- a/packages/next/src/build/index.ts +++ b/packages/next/src/build/index.ts @@ -1098,6 +1098,12 @@ export default async function build( config.experimental.webpackBuildWorker || (config.experimental.webpackBuildWorker === undefined && !config.webpack) + const runServerAndEdgeInParallel = + config.experimental.parallelServerAndEdgeCompiles + const collectServerBuildTracesInParallel = + config.experimental.parallelServerBuildTraces || + (config.experimental.parallelServerBuildTraces === undefined && + isCompileMode) nextBuildSpan.setAttribute( 'has-custom-webpack-config', @@ -1113,43 +1119,65 @@ export default async function build( 'Custom webpack configuration is detected. When using a custom webpack configuration, the Webpack build worker is disabled by default. To force enable it, set the "experimental.webpackBuildWorker" option to "true". Read more: https://nextjs.org/docs/messages/webpack-build-worker-opt-out' ) } + if ( + !useBuildWorker && + (runServerAndEdgeInParallel || collectServerBuildTracesInParallel) + ) { + throw new Error( + 'The "parallelServerBuildTraces" and "parallelServerAndEdgeCompiles" options may only be used when build workers can be used. Read more: https://nextjs.org/docs/messages/parallel-build-without-worker' + ) + } if (!isGenerateMode) { - if (isCompileMode && useBuildWorker) { + if (runServerAndEdgeInParallel || collectServerBuildTracesInParallel) { let durationInSeconds = 0 - await webpackBuild(useBuildWorker, ['server']).then((res) => { + const serverBuildPromise = webpackBuild(useBuildWorker, [ + 'server', + ]).then((res) => { buildTraceContext = res.buildTraceContext durationInSeconds += res.duration - const buildTraceWorker = new Worker( - require.resolve('./collect-build-traces'), - { - numWorkers: 1, - exposedMethods: ['collectBuildTraces'], - } - ) as Worker & typeof import('./collect-build-traces') - buildTracesPromise = buildTraceWorker - .collectBuildTraces({ - dir, - config, - distDir, - // Serialize Map as this is sent to the worker. - pageInfos: serializePageInfos(new Map()), - staticPages: [], - hasSsrAmpPages: false, - buildTraceContext, - outputFileTracingRoot, - }) - .catch((err) => { - console.error(err) - process.exit(1) - }) + if (collectServerBuildTracesInParallel) { + const buildTraceWorker = new Worker( + require.resolve('./collect-build-traces'), + { + numWorkers: 1, + exposedMethods: ['collectBuildTraces'], + } + ) as Worker & typeof import('./collect-build-traces') + + buildTracesPromise = buildTraceWorker + .collectBuildTraces({ + dir, + config, + distDir, + // Serialize Map as this is sent to the worker. + pageInfos: serializePageInfos(new Map()), + staticPages: [], + hasSsrAmpPages: false, + buildTraceContext, + outputFileTracingRoot, + }) + .catch((err) => { + console.error(err) + process.exit(1) + }) + } }) + if (!runServerAndEdgeInParallel) { + await serverBuildPromise + } - await webpackBuild(useBuildWorker, ['edge-server']).then((res) => { + const edgeBuildPromise = webpackBuild(useBuildWorker, [ + 'edge-server', + ]).then((res) => { durationInSeconds += res.duration }) + if (runServerAndEdgeInParallel) { + await serverBuildPromise + } + await edgeBuildPromise await webpackBuild(useBuildWorker, ['client']).then((res) => { durationInSeconds += res.duration diff --git a/packages/next/src/server/config-schema.ts b/packages/next/src/server/config-schema.ts index 69da6035c0a67..588a341dc13f3 100644 --- a/packages/next/src/server/config-schema.ts +++ b/packages/next/src/server/config-schema.ts @@ -285,6 +285,8 @@ export const configSchema: zod.ZodType = z.lazy(() => outputFileTracingIncludes: z .record(z.string(), z.array(z.string())) .optional(), + parallelServerAndEdgeCompiles: z.boolean().optional(), + parallelServerBuildTraces: z.boolean().optional(), ppr: z.boolean().optional(), taint: z.boolean().optional(), proxyTimeout: z.number().gte(0).optional(), diff --git a/packages/next/src/server/config-shared.ts b/packages/next/src/server/config-shared.ts index 2ab2c13167ef7..abc94f0f169f3 100644 --- a/packages/next/src/server/config-shared.ts +++ b/packages/next/src/server/config-shared.ts @@ -282,6 +282,35 @@ export interface ExperimentalConfig { */ typedRoutes?: boolean + /** + * Runs the compilations for server and edge in parallel instead of in serial. + * This will make builds faster if there is enough server and edge functions + * in the application at the cost of more memory. + * + * NOTE: This option is only valid when the build process can use workers. See + * the documentation for `webpackBuildWorker` for more details. + */ + parallelServerAndEdgeCompiles?: boolean + + /** + * Runs the logic to collect build traces for the server routes in parallel + * with other work during the compilation. This will increase the speed of + * the build at the cost of more memory. This option may incur some additional + * work compared to if the option was disabled since the work is started + * before data from the client compilation is available to potentially reduce + * the amount of code that needs to be traced. Despite that, this may still + * result in faster builds for some applications. + * + * Valid values are: + * - `true`: Collect the server build traces in parallel. + * - `false`: Do not collect the server build traces in parallel. + * - `undefined`: Collect server build traces in parallel only in the `experimental-compile` mode. + * + * NOTE: This option is only valid when the build process can use workers. See + * the documentation for `webpackBuildWorker` for more details. + */ + parallelServerBuildTraces?: boolean + /** * Run the Webpack build in a separate process to optimize memory usage during build. * Valid values are: @@ -801,6 +830,8 @@ export const defaultConfig: NextConfig = { typedRoutes: false, instrumentationHook: false, bundlePagesExternals: false, + parallelServerAndEdgeCompiles: false, + parallelServerBuildTraces: false, ppr: // TODO: remove once we've made PPR default // If we're testing, and the `__NEXT_EXPERIMENTAL_PPR` environment variable diff --git a/test/e2e/app-dir/app/next.config.js b/test/e2e/app-dir/app/next.config.js index b316b0726f765..b0617ce870a0f 100644 --- a/test/e2e/app-dir/app/next.config.js +++ b/test/e2e/app-dir/app/next.config.js @@ -1,6 +1,8 @@ module.exports = { experimental: { clientRouterFilterRedirects: true, + parallelServerAndEdgeCompiles: true, + parallelServerBuildTraces: true, webpackBuildWorker: true, }, // output: 'standalone', diff --git a/test/production/app-dir/typed-routes-with-webpack-worker/good-routes/next.config.js b/test/production/app-dir/typed-routes-with-webpack-worker/good-routes/next.config.js index ed7707d2a91a3..ec5729f76a13a 100644 --- a/test/production/app-dir/typed-routes-with-webpack-worker/good-routes/next.config.js +++ b/test/production/app-dir/typed-routes-with-webpack-worker/good-routes/next.config.js @@ -2,6 +2,7 @@ module.exports = { experimental: { typedRoutes: true, + parallelServerBuildTraces: true, webpackBuildWorker: true, }, } From 17a3f7352db082845a8c3f28b9cc5b3fd28cb839 Mon Sep 17 00:00:00 2001 From: Mark Knichel <7355009+mknichel@users.noreply.github.com> Date: Wed, 3 Jan 2024 14:54:56 -0800 Subject: [PATCH 2/5] Add new options to turbopack warning ignore list --- packages/next/src/lib/turbopack-warning.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/next/src/lib/turbopack-warning.ts b/packages/next/src/lib/turbopack-warning.ts index b83cc3a114dff..7caa6f9ad32fa 100644 --- a/packages/next/src/lib/turbopack-warning.ts +++ b/packages/next/src/lib/turbopack-warning.ts @@ -88,6 +88,8 @@ const supportedTurbopackNextConfigOptions = [ 'experimental.memoryBasedWorkersCount', 'experimental.clientRouterFilterRedirects', 'experimental.webpackBuildWorker', + 'experimental.parallelServerAndEdgeCompiles', + 'experimental.parallelServerBuildTraces', 'experimental.appDocumentPreloading', 'experimental.incrementalCacheHandlerPath', 'experimental.amp', From 9988d85c4af91407f891e9721e4bca2fa5fa5bf4 Mon Sep 17 00:00:00 2001 From: Mark Knichel <7355009+mknichel@users.noreply.github.com> Date: Mon, 8 Jan 2024 11:07:26 -0800 Subject: [PATCH 3/5] Rename flag to parallelServerCompiles --- errors/parallel-build-without-worker.mdx | 2 +- packages/next/src/build/index.ts | 4 ++-- packages/next/src/lib/turbopack-warning.ts | 2 +- packages/next/src/server/config-schema.ts | 2 +- packages/next/src/server/config-shared.ts | 4 ++-- test/e2e/app-dir/app/next.config.js | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/errors/parallel-build-without-worker.mdx b/errors/parallel-build-without-worker.mdx index bac156ab7e467..4eb93555829d6 100644 --- a/errors/parallel-build-without-worker.mdx +++ b/errors/parallel-build-without-worker.mdx @@ -4,7 +4,7 @@ title: Parallel Build Without Build Worker ## Why This Error Occurred -The `experimental.parallelServerAndEdgeCompiles` and `experimental.parallelServerBuildTraces` +The `experimental.parallelServerCompiles` and `experimental.parallelServerBuildTraces` options require that the `experimental.webpackBuildWorker` option is set to true. These options use workers to improve the paralellization of the build which may improve performance, but the build may use more memory at the same time. diff --git a/packages/next/src/build/index.ts b/packages/next/src/build/index.ts index 6d31114decc5a..4014a2e8d83c6 100644 --- a/packages/next/src/build/index.ts +++ b/packages/next/src/build/index.ts @@ -1099,7 +1099,7 @@ export default async function build( (config.experimental.webpackBuildWorker === undefined && !config.webpack) const runServerAndEdgeInParallel = - config.experimental.parallelServerAndEdgeCompiles + config.experimental.parallelServerCompiles const collectServerBuildTracesInParallel = config.experimental.parallelServerBuildTraces || (config.experimental.parallelServerBuildTraces === undefined && @@ -1124,7 +1124,7 @@ export default async function build( (runServerAndEdgeInParallel || collectServerBuildTracesInParallel) ) { throw new Error( - 'The "parallelServerBuildTraces" and "parallelServerAndEdgeCompiles" options may only be used when build workers can be used. Read more: https://nextjs.org/docs/messages/parallel-build-without-worker' + 'The "parallelServerBuildTraces" and "parallelServerCompiles" options may only be used when build workers can be used. Read more: https://nextjs.org/docs/messages/parallel-build-without-worker' ) } diff --git a/packages/next/src/lib/turbopack-warning.ts b/packages/next/src/lib/turbopack-warning.ts index 7caa6f9ad32fa..5fcfee4bc88bb 100644 --- a/packages/next/src/lib/turbopack-warning.ts +++ b/packages/next/src/lib/turbopack-warning.ts @@ -88,7 +88,7 @@ const supportedTurbopackNextConfigOptions = [ 'experimental.memoryBasedWorkersCount', 'experimental.clientRouterFilterRedirects', 'experimental.webpackBuildWorker', - 'experimental.parallelServerAndEdgeCompiles', + 'experimental.parallelServerCompiles', 'experimental.parallelServerBuildTraces', 'experimental.appDocumentPreloading', 'experimental.incrementalCacheHandlerPath', diff --git a/packages/next/src/server/config-schema.ts b/packages/next/src/server/config-schema.ts index 588a341dc13f3..c21ddf81170cd 100644 --- a/packages/next/src/server/config-schema.ts +++ b/packages/next/src/server/config-schema.ts @@ -285,7 +285,7 @@ export const configSchema: zod.ZodType = z.lazy(() => outputFileTracingIncludes: z .record(z.string(), z.array(z.string())) .optional(), - parallelServerAndEdgeCompiles: z.boolean().optional(), + parallelServerCompiles: z.boolean().optional(), parallelServerBuildTraces: z.boolean().optional(), ppr: z.boolean().optional(), taint: z.boolean().optional(), diff --git a/packages/next/src/server/config-shared.ts b/packages/next/src/server/config-shared.ts index abc94f0f169f3..441293e20dce8 100644 --- a/packages/next/src/server/config-shared.ts +++ b/packages/next/src/server/config-shared.ts @@ -290,7 +290,7 @@ export interface ExperimentalConfig { * NOTE: This option is only valid when the build process can use workers. See * the documentation for `webpackBuildWorker` for more details. */ - parallelServerAndEdgeCompiles?: boolean + parallelServerCompiles?: boolean /** * Runs the logic to collect build traces for the server routes in parallel @@ -830,7 +830,7 @@ export const defaultConfig: NextConfig = { typedRoutes: false, instrumentationHook: false, bundlePagesExternals: false, - parallelServerAndEdgeCompiles: false, + parallelServerCompiles: false, parallelServerBuildTraces: false, ppr: // TODO: remove once we've made PPR default diff --git a/test/e2e/app-dir/app/next.config.js b/test/e2e/app-dir/app/next.config.js index b0617ce870a0f..0a278d5a58591 100644 --- a/test/e2e/app-dir/app/next.config.js +++ b/test/e2e/app-dir/app/next.config.js @@ -1,7 +1,7 @@ module.exports = { experimental: { clientRouterFilterRedirects: true, - parallelServerAndEdgeCompiles: true, + parallelServerCompiles: true, parallelServerBuildTraces: true, webpackBuildWorker: true, }, From de7b54288238c09997c28f0c9e480d95fd152079 Mon Sep 17 00:00:00 2001 From: mknichel <7355009+mknichel@users.noreply.github.com> Date: Mon, 8 Jan 2024 11:07:54 -0800 Subject: [PATCH 4/5] Update errors/parallel-build-without-worker.mdx Co-authored-by: Delba de Oliveira <32464864+delbaoliveira@users.noreply.github.com> --- errors/parallel-build-without-worker.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/errors/parallel-build-without-worker.mdx b/errors/parallel-build-without-worker.mdx index bac156ab7e467..f6d1516437589 100644 --- a/errors/parallel-build-without-worker.mdx +++ b/errors/parallel-build-without-worker.mdx @@ -5,7 +5,7 @@ title: Parallel Build Without Build Worker ## Why This Error Occurred The `experimental.parallelServerAndEdgeCompiles` and `experimental.parallelServerBuildTraces` -options require that the `experimental.webpackBuildWorker` option is set to true. These +options require that the `experimental.webpackBuildWorker` option is set to `true`. These options use workers to improve the paralellization of the build which may improve performance, but the build may use more memory at the same time. From 2d110446e44a30d8ce177e70c067f9935efd48a5 Mon Sep 17 00:00:00 2001 From: mknichel <7355009+mknichel@users.noreply.github.com> Date: Mon, 8 Jan 2024 11:08:21 -0800 Subject: [PATCH 5/5] Apply suggestions from code review Co-authored-by: Delba de Oliveira <32464864+delbaoliveira@users.noreply.github.com> --- errors/parallel-build-without-worker.mdx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/errors/parallel-build-without-worker.mdx b/errors/parallel-build-without-worker.mdx index f6d1516437589..7d0fb87929c19 100644 --- a/errors/parallel-build-without-worker.mdx +++ b/errors/parallel-build-without-worker.mdx @@ -6,14 +6,12 @@ title: Parallel Build Without Build Worker The `experimental.parallelServerAndEdgeCompiles` and `experimental.parallelServerBuildTraces` options require that the `experimental.webpackBuildWorker` option is set to `true`. These -options use workers to improve the paralellization of the build which may improve performance, +options use workers to improve the parallelization of the build which may improve performance, but the build may use more memory at the same time. ## Possible Ways to Fix It -Build workers are enabled by default unless you have a custom webpack config. -You can force enable the option by setting `config.experimental.webpackBuildWorker: true` in the config, -but some webpack configurations may not be compatible. +Build workers are enabled by default unless you have a custom webpack config. You can force enable the option by setting `experimental.webpackBuildWorker: true` in your `next.config.js` file, but some webpack configuration options may not be compatible. ## Useful Links