Files
Luke SandbergandGitHub 4173ca260e [turbopack] Don't evict when there is little memory to save (#95213)
### What?

Adds a new `'auto'` mode to the experimental `turbopackMemoryEviction`
config option and makes it the default. In `'auto'` mode, Turbopack only
evicts in-memory cache after a snapshot once enough memory has been
allocated since the last eviction to make the work worthwhile.

The option now accepts three values:

- `false`: never evict.
- `'auto'` (new default): evict after a snapshot only once a memory
threshold has been crossed.
- `'full'`: evict all evictable data after every snapshot (the previous
behavior).

### Why?

When the persistent (FileSystem) cache is enabled, Turbopack snapshots
its in-memory state to disk and can then evict those in-memory copies to
reclaim memory, reloading them from disk on demand.

Previously, with eviction enabled (`'full'`), we evicted after *every*
snapshot. This is too aggressive: tasks get restored from disk and then
immediately evicted again, cycle after cycle, wasting work for little
memory benefit.

`'auto'` mirrors the existing persistence-threshold model (we already
skip a snapshot when too little compilation time has accumulated to
justify its cost). Here the proxy is memory instead of time: it isn't
worth paying the restore-then-re-evict churn to reclaim a small amount
of memory.

This was motivated by an example in v0.app where the client would poll
the server every 5 seconds leading to a pathological behavior
* client poll ->
* next.js ensurePage ->
* turbopack writeEndpointToDisk
*  recompute and restore settings for that endpoint (3-10ms of io work)
* realize there is nothing to do
* respond to client
* 2 seconds later..... persist and evict everything saving 0.5M of ram
(100ms of work! writes out 2 SSTs and then compacts them!)
* 3 seconds after that restart the loop

This is silly, #95137 will prevent the persistence loop from occurring ,
this PR will also just skip the 'restore and recompute' work using a
similar strategy.

### How?

We can't measure exactly how much memory a sweep would reclaim, so we
use `TurboMalloc::memory_usage()` (process-global net live bytes) as a
proxy. In `'auto'` mode an eviction sweep runs only once the net bytes
allocated since the last eviction exceed a threshold (default 128 MiB,
overridable via `TURBO_ENGINE_EVICT_MIN_BYTES`). The first eviction
after startup always runs. The threshold scales down under OS memory
pressure (`TurboMalloc::memory_pressure()`) so we evict more eagerly
when memory is tight.

**Backend (`turbo-tasks-backend`):**
- `BackendOptions.evict_after_snapshot: bool` → `eviction_mode:
EvictionMode` (`Off` / `Full` / `Auto`).
- New `EvictionControl` type owns the policy: the mode plus the
threshold bookkeeping. The background snapshot loop calls
`should_evict(snapshot_had_new_data)` once per cycle and
`record_eviction()` after a sweep, so the loop no longer branches on the
mode. `'full'` and `false` behave exactly as before.


<!-- NEXT_JS_LLM_PR -->
2026-07-08 06:04:04 +00:00

47 lines
1.7 KiB
Plaintext

---
title: Turbopack Memory Eviction
nav_title: turbopackMemoryEviction
description: Learn how to control Turbopack's memory eviction strategy for the persistent cache.
---
## Usage
`turbopackMemoryEviction` controls whether Turbopack reclaims memory while the persistent (FileSystem) cache is enabled. After Turbopack writes a snapshot of its cache to disk, it can 'evict' the in-memory copies of that data and reload them from disk on demand.
Currently there are three options
- `false`: never evict. Cached data stays in memory for the lifetime of the process.
- `'auto'` (default): evict after a snapshot only once enough memory has been allocated since the last eviction to make it worthwhile. Leverages thresholds and memory pressure feedback from the operating system.
- `'full'`: evict all possible data from memory every time we save to disk.
> **Good to know:** This option only has an effect in `next dev` sessions when the [FileSystem Cache](/docs/app/api-reference/config/next-config-js/turbopackFileSystemCache) is enabled, since eviction relies on data already being persisted to disk. It is experimental and under active development.
```ts filename="next.config.ts" switcher
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
turbopackMemoryEviction: 'auto',
},
}
export default nextConfig
```
```js filename="next.config.js" switcher
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
turbopackMemoryEviction: 'auto',
},
}
module.exports = nextConfig
```
## Version Changes
| Version | Changes |
| --------- | --------------------------------------------------- |
| `v16.3.0` | `turbopackMemoryEviction` released as experimental. |