Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consolidate Server and Routing process into one process #53523

Merged
merged 53 commits into from Aug 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
ba3d6d6
Remove unused types
timneutkens Aug 3, 2023
56c8daf
Remove useWorkers option (simplifying implementation)
timneutkens Aug 3, 2023
f8f0954
Don't create separate router worker, use the main process
timneutkens Aug 3, 2023
8afa284
Remove sockets variable as server.close() already handlers it.
timneutkens Aug 3, 2023
c3a7663
Don't use underscore for used variables
timneutkens Aug 3, 2023
edb494c
Only initialize workers once.
timneutkens Aug 3, 2023
18007e7
Fix typescript errors
timneutkens Aug 3, 2023
176f7f7
Fix type mismatches
timneutkens Aug 3, 2023
2c790d4
Bring back compression
timneutkens Aug 3, 2023
a03d642
Fix socket connection with custom server
timneutkens Aug 4, 2023
47e0059
Use fixture for test
timneutkens Aug 4, 2023
b563a8d
WIP rework config file watching in dev
timneutkens Aug 4, 2023
968b253
Reboot on config file changes
timneutkens Aug 4, 2023
5c84094
Handle case where cleanup throws
timneutkens Aug 4, 2023
a46589e
Remove unused initializer
timneutkens Aug 4, 2023
4c097e1
Reset test
timneutkens Aug 4, 2023
c6cc0a0
Use temporary redirect for test otherwise the browser caches it.
timneutkens Aug 4, 2023
da891b9
Ensure watcher is attached before booting server so that immediate ch…
timneutkens Aug 4, 2023
50e66f2
Merge branch 'canary' into fix/investigate-memory-usage
timneutkens Aug 5, 2023
828847d
Disable project delete test
timneutkens Aug 7, 2023
74bd66d
Merge branch 'canary' into fix/investigate-memory-usage
timneutkens Aug 7, 2023
342cae0
Merge branch 'fix/investigate-memory-usage' of https://github.com/ver…
timneutkens Aug 7, 2023
71afe95
Update undefined-webpack-config to be more reliable
timneutkens Aug 7, 2023
9fad9dc
Explain checking for nextDev
timneutkens Aug 7, 2023
51a1275
Don't pass initialEnv into worker as it prohibits reloading the env
timneutkens Aug 7, 2023
c42661e
Add type
timneutkens Aug 7, 2023
a3c4248
Fix name of event
timneutkens Aug 7, 2023
c8976ad
Remove unused webpackWatcher variable and stop function
timneutkens Aug 7, 2023
2ed107f
Use consistent setGlobal function (matching other cases)
timneutkens Aug 7, 2023
b130f1f
Revert "Don't pass initialEnv into worker as it prohibits reloading t…
timneutkens Aug 7, 2023
8a30382
Ensure ipc call functions are awaited
timneutkens Aug 7, 2023
9f190ca
Pass initialEnv instead of process.env
timneutkens Aug 7, 2023
05646c2
Fix lint
timneutkens Aug 7, 2023
c773e41
Ensure reloadMatchers is called.
timneutkens Aug 7, 2023
988f860
Skip instrument hmr test
timneutkens Aug 7, 2023
96532db
Remove shouldUseStandaloneMode variable
timneutkens Aug 7, 2023
3d5968f
Rename file to reflect what it is used for
timneutkens Aug 7, 2023
268b7de
Consolidate proxy and server class
timneutkens Aug 7, 2023
78a61eb
Revert "Consolidate proxy and server class"
timneutkens Aug 7, 2023
6f77112
Revert "Rename file to reflect what it is used for"
timneutkens Aug 7, 2023
7ad3a9f
Revert "Remove shouldUseStandaloneMode variable"
timneutkens Aug 7, 2023
22a4321
Return requestHandler to await the promise
timneutkens Aug 7, 2023
4b92a92
Merge branch 'canary' of https://github.com/vercel/next.js into fix/i…
timneutkens Aug 7, 2023
879f9de
fix require cache clearing
ijjk Aug 7, 2023
73ec060
Merge branch 'fix/investigate-memory-usage' of github.com:vercel/next…
ijjk Aug 7, 2023
a454b82
Merge branch 'canary' into fix/investigate-memory-usage
timneutkens Aug 8, 2023
cd58043
Remove proxy
timneutkens Aug 8, 2023
b12dbb9
Merge branch 'fix/investigate-memory-usage' of https://github.com/ver…
timneutkens Aug 8, 2023
f981a7f
Use launchApp to prevent segfault on import()
timneutkens Aug 8, 2023
14a78de
Ensure process exists when the routing worker exits
timneutkens Aug 8, 2023
cda6ef5
Fix lint
timneutkens Aug 8, 2023
bfe6d04
Bring back previous test
timneutkens Aug 8, 2023
f465437
Remove unused variables
timneutkens Aug 8, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
425 changes: 140 additions & 285 deletions packages/next/src/cli/next-dev.ts

Large diffs are not rendered by default.

13 changes: 0 additions & 13 deletions packages/next/src/cli/next-start.ts
Expand Up @@ -5,9 +5,6 @@ import { startServer } from '../server/lib/start-server'
import { getPort, printAndExit } from '../server/lib/utils'
import { getProjectDir } from '../lib/get-project-dir'
import { CliCommand } from '../lib/commands'
import { resolve } from 'path'
import { PHASE_PRODUCTION_SERVER } from '../shared/lib/constants'
import loadConfig from '../server/config'
import { getValidatedArgs } from '../lib/get-validated-args'

const nextStart: CliCommand = async (argv) => {
Expand Down Expand Up @@ -66,22 +63,12 @@ const nextStart: CliCommand = async (argv) => {
? Math.ceil(keepAliveTimeoutArg)
: undefined

const config = await loadConfig(
PHASE_PRODUCTION_SERVER,
resolve(dir || '.'),
undefined,
undefined,
true
)

await startServer({
dir,
nextConfig: config,
isDev: false,
hostname: host,
port,
keepAliveTimeout,
useWorkers: !!config.experimental.appDir,
})
}

Expand Down
4 changes: 4 additions & 0 deletions packages/next/src/server/base-server.ts
Expand Up @@ -468,6 +468,10 @@ export default abstract class Server<ServerOptions extends Options = Options> {
this.responseCache = this.getResponseCache({ dev })
}

protected reloadMatchers() {
return this.matchers.reload()
}

protected async normalizeNextData(
_req: BaseNextRequest,
_res: BaseNextResponse,
Expand Down
10 changes: 0 additions & 10 deletions packages/next/src/server/dev/next-dev-server.ts
Expand Up @@ -87,7 +87,6 @@ export interface Options extends ServerOptions {
export default class DevServer extends Server {
private devReady: Promise<void>
private setDevReady?: Function
private webpackWatcher?: any | null
protected sortedRoutes?: string[]
private pagesDir?: string
private appDir?: string
Expand Down Expand Up @@ -247,15 +246,6 @@ export default class DevServer extends Server {
return 'development'
}

async stopWatcher(): Promise<void> {
if (!this.webpackWatcher) {
return
}

this.webpackWatcher.close()
this.webpackWatcher = null
}

protected async prepareImpl(): Promise<void> {
setGlobal('distDir', this.distDir)
setGlobal('phase', PHASE_DEVELOPMENT_SERVER)
Expand Down
14 changes: 14 additions & 0 deletions packages/next/src/server/lib/is-node-debugging.ts
@@ -0,0 +1,14 @@
export function checkIsNodeDebugging() {
let isNodeDebugging: 'brk' | boolean = !!(
process.execArgv.some((localArg) => localArg.startsWith('--inspect')) ||
process.env.NODE_OPTIONS?.match?.(/--inspect(=\S+)?( |$)/)
)

if (
process.execArgv.some((localArg) => localArg.startsWith('--inspect-brk')) ||
process.env.NODE_OPTIONS?.match?.(/--inspect-brk(=\S+)?( |$)/)
) {
isNodeDebugging = 'brk'
}
return isNodeDebugging
}
19 changes: 6 additions & 13 deletions packages/next/src/server/lib/render-server.ts
Expand Up @@ -3,6 +3,7 @@ import type { RequestHandler } from '../next'
// this must come first as it includes require hooks
import { initializeServerWorker } from './setup-server-worker'
import next from '../next'
import { PropagateToWorkersField } from './router-utils/types'

export const WORKER_SELF_EXIT_CODE = 77

Expand Down Expand Up @@ -39,26 +40,18 @@ export function deleteCache(filePaths: string[]) {
}
}

export async function propagateServerField(field: string, value: any) {
export async function propagateServerField(
field: PropagateToWorkersField,
value: any
) {
if (!app) {
throw new Error('Invariant cant propagate server field, no app initialized')
}
let appField = (app as any).server

if (field.includes('.')) {
const parts = field.split('.')

for (let i = 0; i < parts.length - 1; i++) {
if (appField) {
appField = appField[parts[i]]
}
}
field = parts[parts.length - 1]
}

if (appField) {
if (typeof appField[field] === 'function') {
appField[field].apply(
await appField[field].apply(
(app as any).server,
Array.isArray(value) ? value : []
)
Expand Down
126 changes: 68 additions & 58 deletions packages/next/src/server/lib/router-server.ts
@@ -1,7 +1,10 @@
import type { IncomingMessage } from 'http'

// this must come first as it includes require hooks
import { initializeServerWorker } from './setup-server-worker'
import type {
WorkerRequestHandler,
WorkerUpgradeHandler,
} from './setup-server-worker'

import url from 'url'
import path from 'path'
Expand All @@ -24,6 +27,7 @@ import { getResolveRoutes } from './router-utils/resolve-routes'
import { NextUrlWithParsedQuery, getRequestMeta } from '../request-meta'
import { pathHasPrefix } from '../../shared/lib/router/utils/path-has-prefix'
import { removePathPrefix } from '../../shared/lib/router/utils/remove-path-prefix'
import setupCompression from 'next/dist/compiled/compression'

import {
PHASE_PRODUCTION_SERVER,
Expand All @@ -32,13 +36,6 @@ import {
} from '../../shared/lib/constants'
import { signalFromNodeResponse } from '../web/spec-extension/adapters/next-request'

let initializeResult:
| undefined
| {
port: number
hostname: string
}

const debug = setupDebug('next:router-server:main')

export type RenderWorker = InstanceType<
Expand All @@ -51,6 +48,13 @@ export type RenderWorker = InstanceType<
propagateServerField: typeof import('./render-server').propagateServerField
}

const nextDeleteCacheCallbacks: Array<(filePaths: string[]) => Promise<any>> =
[]
const nextDeleteAppClientCacheCallbacks: Array<() => Promise<any>> = []
const nextClearModuleContextCallbacks: Array<
(targetPath: string) => Promise<any>
> = []

export async function initialize(opts: {
dir: string
port: number
Expand All @@ -61,10 +65,7 @@ export async function initialize(opts: {
isNodeDebugging: boolean
keepAliveTimeout?: number
customServer?: boolean
}): Promise<NonNullable<typeof initializeResult>> {
if (initializeResult) {
return initializeResult
}
}): Promise<[WorkerRequestHandler, WorkerUpgradeHandler]> {
process.title = 'next-router-worker'

if (!process.env.NODE_ENV) {
Expand All @@ -80,6 +81,12 @@ export async function initialize(opts: {
true
)

let compress: ReturnType<typeof setupCompression> | undefined

if (config?.compress !== false) {
compress = setupCompression()
}

const fsChecker = await setupFsCheck({
dev: opts.dev,
dir: opts.dir,
Expand Down Expand Up @@ -207,39 +214,57 @@ export async function initialize(opts: {
)

// pre-initialize workers
await renderWorkers.app?.initialize(renderWorkerOpts)
await renderWorkers.pages?.initialize(renderWorkerOpts)
const initialized = {
app: await renderWorkers.app?.initialize(renderWorkerOpts),
pages: await renderWorkers.pages?.initialize(renderWorkerOpts),
}

if (devInstance) {
Object.assign(devInstance.renderWorkers, renderWorkers)

nextDeleteCacheCallbacks.push((filePaths: string[]) =>
Promise.all([
renderWorkers.pages?.deleteCache(filePaths),
renderWorkers.app?.deleteCache(filePaths),
])
)
nextDeleteAppClientCacheCallbacks.push(() =>
Promise.all([
renderWorkers.pages?.deleteAppClientCache(),
renderWorkers.app?.deleteAppClientCache(),
])
)
nextClearModuleContextCallbacks.push((targetPath: string) =>
Promise.all([
renderWorkers.pages?.clearModuleContext(targetPath),
renderWorkers.app?.clearModuleContext(targetPath),
])
)
;(global as any)._nextDeleteCache = async (filePaths: string[]) => {
try {
await Promise.all([
renderWorkers.pages?.deleteCache(filePaths),
renderWorkers.app?.deleteCache(filePaths),
])
} catch (err) {
console.error(err)
for (const cb of nextDeleteCacheCallbacks) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should those callbacks be parallelised?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ijjk it's unclear to me why these are arrays, seems they're all arrays with only one item? 🤔

try {
await cb(filePaths)
} catch (err) {
console.error(err)
}
}
}
;(global as any)._nextDeleteAppClientCache = async () => {
try {
await Promise.all([
renderWorkers.pages?.deleteAppClientCache(),
renderWorkers.app?.deleteAppClientCache(),
])
} catch (err) {
console.error(err)
for (const cb of nextDeleteAppClientCacheCallbacks) {
try {
await cb()
} catch (err) {
console.error(err)
}
}
}
;(global as any)._nextClearModuleContext = async (targetPath: string) => {
try {
await Promise.all([
renderWorkers.pages?.clearModuleContext(targetPath),
renderWorkers.app?.clearModuleContext(targetPath),
])
} catch (err) {
console.error(err)
for (const cb of nextClearModuleContextCallbacks) {
try {
await cb(targetPath)
} catch (err) {
console.error(err)
}
}
}
}
Expand Down Expand Up @@ -274,10 +299,11 @@ export async function initialize(opts: {
devInstance?.ensureMiddleware
)

const requestHandler: Parameters<typeof initializeServerWorker>[0] = async (
req,
res
) => {
const requestHandler: WorkerRequestHandler = async (req, res) => {
if (compress) {
// @ts-expect-error not express req/res
compress(req, res, () => {})
}
req.on('error', (_err) => {
// TODO: log socket errors?
})
Expand Down Expand Up @@ -319,8 +345,7 @@ export async function initialize(opts: {
return null
}

const curWorker = renderWorkers[type]
const workerResult = await curWorker?.initialize(renderWorkerOpts)
const workerResult = initialized[type]

if (!workerResult) {
throw new Error(`Failed to initialize render worker ${type}`)
Expand Down Expand Up @@ -690,11 +715,7 @@ export async function initialize(opts: {
}
}

const upgradeHandler: Parameters<typeof initializeServerWorker>[1] = async (
req,
socket,
head
) => {
const upgradeHandler: WorkerUpgradeHandler = async (req, socket, head) => {
try {
req.on('error', (_err) => {
// TODO: log socket errors?
Expand Down Expand Up @@ -735,16 +756,5 @@ export async function initialize(opts: {
}
}

const { port, hostname } = await initializeServerWorker(
requestHandler,
upgradeHandler,
opts
)

initializeResult = {
port,
hostname: hostname === '0.0.0.0' ? '127.0.0.1' : hostname,
}

return initializeResult
return [requestHandler, upgradeHandler]
}
13 changes: 7 additions & 6 deletions packages/next/src/server/lib/router-utils/setup-dev.ts
Expand Up @@ -17,7 +17,7 @@ import findUp from 'next/dist/compiled/find-up'
import { buildCustomRoute } from './filesystem'
import * as Log from '../../../build/output/log'
import HotReloader, { matchNextPageBundleRequest } from '../../dev/hot-reloader'
import { traceGlobals } from '../../../trace/shared'
import { setGlobal } from '../../../trace/shared'
import { Telemetry } from '../../../telemetry/storage'
import { IncomingMessage, ServerResponse } from 'http'
import loadJsConfig from '../../../build/load-jsconfig'
Expand Down Expand Up @@ -79,6 +79,7 @@ import { PagesManifest } from '../../../build/webpack/plugins/pages-manifest-plu
import { AppBuildManifest } from '../../../build/webpack/plugins/app-build-manifest-plugin'
import { PageNotFoundError } from '../../../shared/lib/utils'
import { srcEmptySsgManifest } from '../../../build/webpack/plugins/build-manifest-plugin'
import { PropagateToWorkersField } from './types'
import { MiddlewareManifest } from '../../../build/webpack/plugins/middleware-plugin'

type SetupOpts = {
Expand Down Expand Up @@ -120,8 +121,8 @@ async function startWatcher(opts: SetupOpts) {

const distDir = path.join(opts.dir, opts.nextConfig.distDir)

traceGlobals.set('distDir', distDir)
traceGlobals.set('phase', PHASE_DEVELOPMENT_SERVER)
setGlobal('distDir', distDir)
setGlobal('phase', PHASE_DEVELOPMENT_SERVER)

const validFileMatcher = createValidFileMatcher(
nextConfig.pageExtensions,
Expand All @@ -133,7 +134,7 @@ async function startWatcher(opts: SetupOpts) {
pages?: import('../router-server').RenderWorker
} = {}

async function propagateToWorkers(field: string, args: any) {
async function propagateToWorkers(field: PropagateToWorkersField, args: any) {
await renderWorkers.app?.propagateServerField(field, args)
await renderWorkers.pages?.propagateServerField(field, args)
}
Expand Down Expand Up @@ -1010,7 +1011,7 @@ async function startWatcher(opts: SetupOpts) {
hotReloader.setHmrServerError(new Error(errorMessage))
} else if (numConflicting === 0) {
hotReloader.clearHmrServerError()
await propagateToWorkers('matchers.reload', undefined)
await propagateToWorkers('reloadMatchers', undefined)
}
}

Expand Down Expand Up @@ -1279,7 +1280,7 @@ async function startWatcher(opts: SetupOpts) {
} finally {
// Reload the matchers. The filesystem would have been written to,
// and the matchers need to re-scan it to update the router.
await propagateToWorkers('middleware.reload', undefined)
await propagateToWorkers('reloadMatchers', undefined)
}
})

Expand Down
7 changes: 7 additions & 0 deletions packages/next/src/server/lib/router-utils/types.ts
@@ -0,0 +1,7 @@
export type PropagateToWorkersField =
| 'actualMiddlewareFile'
| 'actualInstrumentationHookFile'
| 'reloadMatchers'
| 'loadEnvConfig'
| 'appPathRoutes'
| 'middleware'