mirror of
https://github.com/hanzoai/next.js.git
synced 2026-10-30 14:46:38 +00:00
Applications can now override the `'use cache'` fill timeout through a new `experimental.useCacheTimeout` config (in seconds, matching the `staticPageGenerationTimeout` / `cacheLife` configs), which lets them raise the timeout for slower dev-time backends or lower it for faster feedback while iterating. Without a value set, the timeout defaults to 90% of `staticPageGenerationTimeout`, landing at 54s against the 60s default build timeout — slightly higher than the previous hard-coded 50s. During static and runtime prerendering, the effective timeout is clamped to `0.9 × staticPageGenerationTimeout` so the cache-fill error surfaces before the build worker kills the page. In dev mode, the configured value is used as-is since there's no equivalent build-worker kill. The defaulting and clamp are centralized in a single `getUseCacheFillTimeoutMs` helper in `use-cache-wrapper.ts`, replacing the previous module-level `USE_CACHE_FILL_TIMEOUT_MS` constant. The new value is threaded through `RenderOpts` and `WorkStore`. Proxy/middleware and the edge route-module wrapper construct a WorkStore but never fill `'use cache'` entries, so they pass `0` sentinels with a comment — if anything ever reads these values, the cache will time out immediately and surface the bug loudly. We'll revisit this if we ever add `'use cache'` support to Proxy. A new `use-cache-configured-timeout` fixture exercises the clamp by setting `staticPageGenerationTimeout: 10` and flipping `useCacheTimeout` between `15` (in dev, via `process.env.__NEXT_DEV_SERVER`) and `60` (in build). Two pages, one sleeping 11s (between the 9s clamp and the 15s dev timeout) and one sleeping 17s (above the dev timeout), let us assert that the dev value is applied without clamping and that the build clamps both pages. The existing `use-cache-hanging` fixture now sets `useCacheTimeout: 10` so tests don't race the browser's `page.goto` ceiling, and the explicit 180s test timeouts there have been dropped in favor of the Jest default.
81 lines
2.9 KiB
TypeScript
81 lines
2.9 KiB
TypeScript
import { nextTestSetup } from 'e2e-utils'
|
|
import stripAnsi from 'strip-ansi'
|
|
|
|
const expectedTimeoutErrorMessage =
|
|
'Filling a cache during prerender timed out, likely because request-specific arguments such as params, searchParams, cookies() or dynamic data were used inside "use cache".'
|
|
|
|
describe('use-cache-configured-timeout', () => {
|
|
const { next, isNextDev, skipped } = nextTestSetup({
|
|
files: __dirname,
|
|
skipDeployment: true,
|
|
skipStart: process.env.NEXT_TEST_MODE !== 'dev',
|
|
})
|
|
|
|
if (skipped) {
|
|
return
|
|
}
|
|
|
|
if (isNextDev) {
|
|
describe('when a "use cache" fill is below the configured dev `useCacheTimeout`', () => {
|
|
it('should not clamp the dev timeout and allow the cache fill to complete', async () => {
|
|
const outputIndex = next.cliOutput.length
|
|
const browser = await next.browser('/below-dev-timeout')
|
|
|
|
await expect(browser.elementByCss('#result').text()).resolves.toBe(
|
|
'cached'
|
|
)
|
|
|
|
const cliOutput = stripAnsi(next.cliOutput.slice(outputIndex))
|
|
|
|
expect(cliOutput).not.toContain(expectedTimeoutErrorMessage)
|
|
})
|
|
})
|
|
|
|
describe('when a "use cache" fill exceeds the configured dev `useCacheTimeout`', () => {
|
|
it('should apply the configured timeout and show the error', async () => {
|
|
const outputIndex = next.cliOutput.length
|
|
const browser = await next.browser('/above-dev-timeout')
|
|
|
|
await expect(browser).toDisplayRedbox(`
|
|
{
|
|
"code": "E394",
|
|
"description": "Filling a cache during prerender timed out, likely because request-specific arguments such as params, searchParams, cookies() or dynamic data were used inside "use cache".",
|
|
"environmentLabel": null,
|
|
"label": "Runtime Error",
|
|
"source": "app/above-dev-timeout/page.tsx (4:1) @ getCachedData
|
|
> 4 | async function getCachedData(): Promise<string> {
|
|
| ^",
|
|
"stack": [
|
|
"getCachedData app/above-dev-timeout/page.tsx (4:1)",
|
|
"Cached app/above-dev-timeout/page.tsx (13:22)",
|
|
"Page app/above-dev-timeout/page.tsx (19:10)",
|
|
],
|
|
}
|
|
`)
|
|
|
|
const cliOutput = stripAnsi(next.cliOutput.slice(outputIndex))
|
|
|
|
expect(cliOutput).toContain(expectedTimeoutErrorMessage)
|
|
})
|
|
})
|
|
} else {
|
|
describe('when `experimental.useCacheTimeout` exceeds `staticPageGenerationTimeout` during prerendering', () => {
|
|
it('should clamp the build timeout and fail both pages with a timeout error', async () => {
|
|
try {
|
|
await next.start()
|
|
} catch {
|
|
// expected
|
|
}
|
|
|
|
expect(next.cliOutput).toContain(expectedTimeoutErrorMessage)
|
|
expect(next.cliOutput).toContain(
|
|
'Error occurred prerendering page "/below-dev-timeout"'
|
|
)
|
|
expect(next.cliOutput).toContain(
|
|
'Error occurred prerendering page "/above-dev-timeout"'
|
|
)
|
|
})
|
|
})
|
|
}
|
|
})
|