Skip to content

Commit

Permalink
perf: refactor path logic in router + add LRU cache (vercel#41365)
Browse files Browse the repository at this point in the history
I'm investigating the runtime perf of the node server and this was one of the hot spot I stumbled upon.

This diff:
- refactors getPagePath to not throw when it doesn't find a path: this function is used to check if a path exists, we don't want that kind of overhead there
- adds a LRU cache to cache the result of the evaluation

Results:

before:
> 110k requests in 11.01s, 285 MB read

after
>135k requests in 11.01s, 348 MB read

on an autocannon run



## Bug

- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Errors have a helpful link attached, see `contributing.md`

## Feature

- [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have a helpful link attached, see `contributing.md`

## Documentation / Examples

- [ ] Make sure the linting passes by running `pnpm lint`
- [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
  • Loading branch information
feedthejim authored and Kikobeats committed Oct 24, 2022
1 parent 8bcd860 commit 2717c9f
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 10 deletions.
14 changes: 7 additions & 7 deletions packages/next/server/next-server.ts
Expand Up @@ -74,7 +74,7 @@ import BaseServer, {
NoFallbackError,
RequestContext,
} from './base-server'
import { getPagePath, requireFontManifest } from './require'
import { getMaybePagePath, getPagePath, requireFontManifest } from './require'
import { denormalizePagePath } from '../shared/lib/page-path/denormalize-page-path'
import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path'
import { loadComponents } from './load-components'
Expand Down Expand Up @@ -327,12 +327,12 @@ export default class NextNodeServer extends BaseServer {
}

protected async hasPage(pathname: string): Promise<boolean> {
let found = false
try {
found = !!this.getPagePath(pathname, this.nextConfig.i18n?.locales)
} catch (_) {}

return found
return !!getMaybePagePath(
pathname,
this.distDir,
this.nextConfig.i18n?.locales,
this.hasAppDir
)
}

protected getBuildId(): string {
Expand Down
46 changes: 43 additions & 3 deletions packages/next/server/require.ts
Expand Up @@ -11,13 +11,33 @@ import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path'
import { denormalizePagePath } from '../shared/lib/page-path/denormalize-page-path'
import type { PagesManifest } from '../build/webpack/plugins/pages-manifest-plugin'
import { PageNotFoundError, MissingStaticPage } from '../shared/lib/utils'
import LRUCache from 'next/dist/compiled/lru-cache'

export function getPagePath(
const pagePathCache =
process.env.NODE_ENV === 'development'
? {
get: (_key: string) => {
return null
},
set: () => {},
has: () => false,
}
: new LRUCache<string, string | null>({
max: 1000,
})

export function getMaybePagePath(
page: string,
distDir: string,
locales?: string[],
appDirEnabled?: boolean
): string {
): string | null {
const cacheKey = `${page}:${locales}`

if (pagePathCache.has(cacheKey)) {
return pagePathCache.get(cacheKey) as string | null
}

const serverBuildPath = join(distDir, SERVER_DIRECTORY)
let appPathsManifest: undefined | PagesManifest

Expand Down Expand Up @@ -60,10 +80,30 @@ export function getPagePath(
pagePath = checkManifest(pagesManifest)
}

if (!pagePath) {
pagePathCache.set(cacheKey, null)
return null
}

const path = join(serverBuildPath, pagePath)
pagePathCache.set(cacheKey, path)

return path
}

export function getPagePath(
page: string,
distDir: string,
locales?: string[],
appDirEnabled?: boolean
): string {
const pagePath = getMaybePagePath(page, distDir, locales, appDirEnabled)

if (!pagePath) {
throw new PageNotFoundError(page)
}
return join(serverBuildPath, pagePath)

return pagePath
}

export function requirePage(
Expand Down

0 comments on commit 2717c9f

Please sign in to comment.