mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
Currently we skip a persist cycle when there are no dirty tasks. This PR extends that to skip persist cycles when little compilation work has happened since the last snapshot, avoiding small writes and expensive compaction for little benefit. Compilation time is a better proxy for "how much work a snapshot would save" than a raw dirty-task count. The background snapshot loop accumulates active (non-idle) wall-clock time and skips a cycle when less than a threshold has elapsed since the last persisted snapshot. Default is 1s, overridable via `TURBO_ENGINE_SNAPSHOT_MIN_ACTIVE_TIME_MILLIS` (set to `0` in tests). Shutdown and test snapshots bypass the gate. The active-time bookkeeping is encapsulated in a small `Stopwatch` helper. <!-- NEXT_JS_LLM_PR -->
95 lines
2.7 KiB
TypeScript
95 lines
2.7 KiB
TypeScript
import { nextTestSetup, isNextDev } from 'e2e-utils'
|
|
import { retry, waitFor } from 'next-test-utils'
|
|
|
|
// Eviction requires the dev server (HMR) and persistent caching (Turbopack).
|
|
// Skip entirely in prod/start mode.
|
|
;(isNextDev ? describe : describe.skip)('evict-after-snapshot', () => {
|
|
const envVars = [
|
|
'ENABLE_CACHING=1',
|
|
'TURBO_ENGINE_IGNORE_DIRTY=1',
|
|
'TURBO_ENGINE_SNAPSHOT_IDLE_TIMEOUT_MILLIS=1000',
|
|
// Persist even tiny snapshots so the test doesn't depend on the
|
|
// minimum-compilation-time threshold.
|
|
'TURBO_ENGINE_SNAPSHOT_MIN_ACTIVE_TIME_MILLIS=0',
|
|
'ENABLE_EVICTION=1',
|
|
].join(' ')
|
|
|
|
const { skipped, next } = nextTestSetup({
|
|
files: __dirname,
|
|
skipDeployment: true,
|
|
packageJson: {
|
|
scripts: {
|
|
dev: `${envVars} next dev`,
|
|
},
|
|
},
|
|
installCommand: 'npm i',
|
|
startCommand: 'npm run dev',
|
|
})
|
|
|
|
if (skipped) {
|
|
return
|
|
}
|
|
|
|
async function waitForSnapshotAndEviction() {
|
|
// The idle timeout is 1s, give extra time for snapshot + eviction to complete
|
|
await waitFor(5000)
|
|
}
|
|
|
|
// Turbopack-only: eviction requires persistent caching
|
|
;(process.env.IS_TURBOPACK_TEST ? it : it.skip)(
|
|
'should serve correct content after eviction and HMR',
|
|
async () => {
|
|
const browser = await next.browser('/')
|
|
await retry(async () => {
|
|
expect(await browser.elementByCss('p').text()).toBe('hello world')
|
|
})
|
|
|
|
let currentContent = 'hello world'
|
|
for (let cycle = 1; cycle <= 3; cycle++) {
|
|
await waitForSnapshotAndEviction()
|
|
|
|
const prevContent = currentContent
|
|
const nextContent = `cycle ${cycle}`
|
|
await next.patchFile('app/page.tsx', (content) =>
|
|
content.replace(prevContent, nextContent)
|
|
)
|
|
currentContent = nextContent
|
|
|
|
const expected = currentContent
|
|
await retry(async () => {
|
|
expect(await browser.elementByCss('p').text()).toBe(expected)
|
|
}, 10000)
|
|
}
|
|
|
|
await browser.close()
|
|
},
|
|
90000
|
|
)
|
|
;(process.env.IS_TURBOPACK_TEST ? it : it.skip)(
|
|
'should handle client component HMR after eviction',
|
|
async () => {
|
|
const browser = await next.browser('/client')
|
|
await retry(async () => {
|
|
expect(await browser.elementByCss('p').text()).toBe('hello world')
|
|
})
|
|
|
|
await waitForSnapshotAndEviction()
|
|
|
|
await next.patchFile(
|
|
'app/client/page.tsx',
|
|
(content) => content.replace('hello world', 'hello eviction'),
|
|
async () => {
|
|
await retry(async () => {
|
|
expect(await browser.elementByCss('p').text()).toBe(
|
|
'hello eviction'
|
|
)
|
|
}, 10000)
|
|
}
|
|
)
|
|
|
|
await browser.close()
|
|
},
|
|
90000
|
|
)
|
|
})
|