---
title: "Function Caching"
description: "Function caching provides robust execution wrapping, key encoding, and response streaming capabilities for 'use cache' operations within the App Router architecture. It addresses challenges related..."
last_updated: "2026-09-23T10:52:03.199778+00:00"
canonical_url: "https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/caching-and-export/function-caching"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [packages/next/src/server/use-cache/use-cache-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts)
- [packages/next/src/server/web/spec-extension/unstable-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts)
- [packages/next/src/server/route-modules/route-module.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/route-module.ts)
- [packages/next/src/server/lib/patch-fetch.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/patch-fetch.ts)
- [packages/next/src/server/dev/use-cache-probe-worker.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-worker.ts)
- [packages/next/src/server/route-modules/app-route/module.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts)
- [packages/next/src/server/dev/on-demand-entry-handler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/on-demand-entry-handler.ts)
- [packages/next/src/server/dev/use-cache-probe-pool.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-pool.ts)
- [packages/next/src/server/response-cache/index.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts)
- [packages/next/src/lib/coalesced-function.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/coalesced-function.ts)
- [packages/next/src/lib/with-promise-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/with-promise-cache.ts)
- [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts)
- [packages/next/src/server/web/sandbox/sandbox.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts)
- [packages/next/src/server/use-cache/use-cache-probe-globals.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-probe-globals.ts)
- [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts)
- [packages/next/src/server/next-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts)
- [packages/next/src/server/web/edge-route-module-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts)
</details>

## Overview

Function caching provides robust execution wrapping, key encoding, and response streaming capabilities for `'use cache'` operations within the App Router architecture. It addresses challenges related to concurrent function re-executions, storage isolation, and request context preservation across both Node.js server and Edge runtime environments. Key design decisions involve leveraging LRU-based memory stores, asynchronous worker pools for state exploration, and interoperability layers with legacy caching mechanisms like `unstable_cache`.

Sources: [packages/next/src/server/use-cache/use-cache-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L1619-L1653), [packages/next/src/server/web/spec-extension/unstable-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts#L61-L71), [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L44-L61), [packages/next/src/server/dev/use-cache-probe-pool.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-pool.ts#L63-L122), [packages/next/src/server/web/edge-route-module-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L35-L50)

## Function Cache Wrapper Architecture

### Overview

Function caching wraps `'use cache'` invocations to enforce execution constraints, route lookup requests through registered cache handlers, and manage React flight stream caching. When a cached function is invoked, the wrapper validates the active work store context, resolves the appropriate storage backend, and coordinates retrieval or re-execution.

Sources: [packages/next/src/server/use-cache/use-cache-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L1619-L1653), [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L97-L110)

### Execution Call-Chain Walkthrough

The core retrieval and execution path flows through specific module boundaries during a cache lookup operation.

1. `cache` (`packages/next/src/server/use-cache/use-cache-wrapper.ts`): Intercepts the function call, validates the `workAsyncStorage` and `workUnitStore` contexts, determines whether the function is private or public, constructs cache contexts, and invokes handler resolution.
Sources: [packages/next/src/server/use-cache/use-cache-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L1619-L1653)
2. `getCacheHandler` (`packages/next/src/server/use-cache/handlers.ts`): Queries the global `handlersMapSymbol` map using the specified cache kind (e.g., `'default'` or `'remote'`), throwing an error if cache handlers have not been initialized or returning the requested `CacheHandler` instance.
Sources: [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L103-L110)
3. `get` (`packages/next/src/server/lib/cache-handlers/default.ts`): Executes on the resolved `CacheHandler` instance, checking pending promises, evaluating LRU memory store entries, validating expiration timestamps or tags (`areTagsExpired`, `areTagsStale`), and teeing the underlying `ReadableStream` for safe consumption.
Sources: [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L69-L128)

```mermaid
sequenceDiagram
    participant W as cache (use-cache-wrapper.ts)
    participant H as getCacheHandler (handlers.ts)
    participant C as get (default.ts)

    W->>H: getCacheHandler(kind)
    H-->>W: CacheHandler instance
    W->>C: handler.get(cacheKey)
    C-->>W: Cached entry or undefined
```

Sources: [packages/next/src/server/use-cache/use-cache-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L1619-L1653), [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L103-L110), [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L69-L128)

### Cache Context and Work Unit States

The wrapper inspects the active `workUnitStore.type` to enforce rules regarding where `'use cache'` and `'use cache: private'` expressions can execute.

| Work Unit Store Type | Public `'use cache'` Behavior | Private `'use cache: private'` Behavior |
| :--- | :--- | :--- |
| `prerender` | Executes render tracking and static generation | Returns hanging promise via `makeHangingPromise` |
| `prerender-ppr` | Postpones execution via dynamic tracking | Postpones execution via `postponeWithTracking` |
| `prerender-legacy` | Throws interrupt static generation | Throws via `throwToInterruptStaticGeneration` |
| `prerender-client` / `validation-client` | Throws `InvariantError` (forbidden in client components) | Throws `InvariantError` (forbidden in client components) |
| `cache` | Creates nested dynamic cache error context | Throws invalid dynamic usage error |
| `unstable_cache` | Allowed / standard nesting | Throws invalid dynamic usage error |
| `request` / `prerender-runtime` / `private-cache` | Standard cache context creation | Standard private cache context creation |

Sources: [packages/next/src/server/use-cache/use-cache-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L1698-L1801)

> [!WARNING]
> Using `'use cache: private'` within an `unstable_cache()` block or a nested public `'use cache'` function throws an `InvalidDynamicUsageError` during execution because private state cannot be safely nested inside shared caching boundaries.
> Sources: [packages/next/src/server/use-cache/use-cache-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L1723-L1738)

### Design Trade-Offs

| Design Choice | Benefit | Cost |
| :--- | :--- | :--- |
| Global symbol storage for cache handlers (`@next/cache-handlers`) | Shares cache handler instances across distinct module copies and boundary boundaries | Relies on global singleton state which complicates isolated unit testing |
| Teeing streams on cache hit (`entry.value.tee()`) | Allows concurrent readers to consume independent clones of the cached `ReadableStream` | Increases memory overhead by buffering chunks until both branches are consumed |
| Pending promise deduplication map (`pendingSets`) | Prevents cache stampedes and duplicate concurrent writes for the same cache key | Holds promises in memory until concurrent set operations settle |

Sources: [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L10-L29), [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L58-L75), [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L113-L115)

## Cache Handler Initialization and Resolution

### Overview

Cache handler initialization and resolution manage how Next.js sets up, loads, and routes requests to default or custom cache handlers within Node.js server runtimes. Handlers are stored globally using specific symbols (`@next/cache-handlers`, `@next/cache-handlers-map`, `@next/cache-handlers-set`, and `@next/cache-handlers-private`) to guarantee that identical cache instances remain accessible across different module copies and boundary lines.
Sources: [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L10-L29)

### Initialization and Custom Handler Loading

The initialization sequence sets up global maps and fallback handlers, loading user-defined modules when specified in the runtime configuration.

```mermaid
sequenceDiagram
    participant S as NextNodeServer (next-server.ts)
    participant RM as RouteModule (route-module.ts)
    participant H as handlers.ts
    participant D as default.ts

    S->>RM: unstable_preloadEntries() / getIncrementalCache()
    RM->>H: initializeCacheHandlers(cacheMaxMemorySize)
    H->>D: createDefaultCacheHandler(cacheMaxMemorySize)
    D-->>H: Default cache handler instance
    H-->>RM: Initialized maps & sets
    RM->>H: setCacheHandler(kind, customHandler)
```

Sources: [packages/next/src/server/route-modules/route-module.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/route-module.ts#L433-L471), [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L37-L95), [packages/next/src/server/next-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L294-L338)

The call-chain for handler loading proceeds through distinct phases: `constructor` triggers `unstable_preloadEntries`, which calls `loadCustomCacheHandlers`, invoking `initializeCacheHandlers`, followed by `set` and `resolvePending`.

1. `constructor` initiates server lifecycle setup and triggers preloading when not in development mode.
Sources: [packages/next/src/server/next-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L200-L236)
2. `unstable_preloadEntries` awaits `prepare()` and invokes custom cache handlers before preloading components.
Sources: [packages/next/src/server/next-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L294-L302)
3. `loadCustomCacheHandlers` checks `cacheHandlers` configuration and checks whether initialization has occurred.
Sources: [packages/next/src/server/route-modules/route-module.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/route-module.ts#L433-L444)
4. `initializeCacheHandlers` allocates the global handlers map and sets up default or symbol-backed handlers.
Sources: [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L37-L95)
5. `set` (via `setCacheHandler`) populates the cache handlers map and set with resolved custom handlers.
Sources: [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L161-L173)
6. `resolvePending` settles active promises inside `pendingSets` once storage operations complete.
Sources: [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L130-L167)

> [!WARNING]
> Calling `getCacheHandler()`, `getPrivateCacheHandler()`, or `setCacheHandler()` before `initializeCacheHandlers()` has executed will throw an explicit error stating `'Cache handlers not initialized'`.
> Sources: [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L103-L125), [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L161-L168)

### Cache Handler Symbols and Accessors

| Symbol Name | Global Key | Purpose |
| :--- | :--- | :--- |
| `handlersSymbol` | `@next/cache-handlers` | Stores raw pre-existing `RemoteCache` or `DefaultCache` references on `globalThis` |
| `handlersMapSymbol` | `@next/cache-handlers-map` | Maps string kinds (such as `'default'` and `'remote'`) to active `CacheHandler` instances |
| `handlersSetSymbol` | `@next/cache-handlers-set` | Maintains a unique `Set` of all active `CacheHandler` instances |
| `privateHandlerSymbol` | `@next/cache-handlers-private` | Dev-only dedicated in-memory cache handler for private cache entries |

Sources: [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L10-L29), [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L89-L92)

> [!NOTE]
> The private cache handler is intentionally stored outside the kind-keyed map on `[privateHandlerSymbol]` so that user-configured custom cache handlers can never inadvertently intercept or replace request-specific private cache storage.
> Sources: [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L112-L125)

### Design Trade-Offs

| Design Choice | Benefit | Cost |
| :--- | :--- | :--- |
| Gating private cache initialization on `process.env.__NEXT_DEV_SERVER` | Avoids persisting request-derived private cache data to shared production stores | Increases dev-mode memory footprint by maintaining a separate LRU instance |
| Dynamic ESM imports via `dynamicImportEsmDefault` and `formatDynamicImportPath` | Supports flexible path resolution and interop wrapping for custom user cache handlers | Introduces asynchronous module loading overhead during server preparation |
| Bypassing cache initialization when `cacheMaxMemorySize` equals `0` | Eliminates unnecessary LRU cache instantiation and memory allocation | Disables local memory caching entirely for that handler instance |

Sources: [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L89-L93), [packages/next/src/server/route-modules/route-module.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/route-module.ts#L433-L471), [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L44-L56)

## Edge Runtime Cache Handler Integration

### Edge Runtime Cache Handler Integration

### Overview

The `EdgeRouteModuleWrapper` class manages route execution specifically within the edge runtime. During an incoming request handling cycle, it initializes cache handlers using configuration retrieved from the route module and binds custom cache handlers before invoking the underlying route module handler.
Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L31-L105)

```mermaid
sequenceDiagram
    participant EdgeRouteModuleWrapper as EdgeRouteModuleWrapper
    participant Handlers as initializeCacheHandlers
    participant SetHandler as setCacheHandler
    participant DefaultCache as resolvePending
    EdgeRouteModuleWrapper->>Handlers: handler -> initializeCacheHandlers(nextConfig.cacheMaxMemorySize)
    Handlers->>SetHandler: set -> setCacheHandler(kind, cacheHandler)
    SetHandler->>DefaultCache: resolvePending -> resolvePending()
```
Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L101-L104), [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L37-L95), [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L161-L173), [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L130-L167)

### Call-Chain Execution Walkthrough

1. `handler` — The private `handler` method executes upon receiving an incoming `NextRequestHint` and `NextFetchEvent`, fetches edge configuration via `this.routeModule.getNextConfigEdge()`, and calls `initializeCacheHandlers(nextConfig.cacheMaxMemorySize)`.
   Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L84-L101)
2. `initializeCacheHandlers` — Allocates the global handlers map on `globalThis`, seeds default or remote handlers, and configures fallback mechanisms.
   Sources: [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L37-L95)
3. `set` — Iterates over `this.cacheHandlers` entries within the edge wrapper, invoking `setCacheHandler(kind, cacheHandler)` to bind each custom handler into the global map and set.
   Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L102-L104), [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L161-L173)
4. `resolvePending` — When cache set operations occur within cache handlers, active `pendingSets` promises are registered and subsequently resolved via `resolvePending()` once stream chunk reading and caching finalize.
   Sources: [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L130-L167)

> [!WARNING]
> The edge runtime does not support Cache Components or static page generation; `useCacheTimeout` and `staticPageGenerationTimeout` are explicitly set to `0` as sentinels to surface configuration bugs immediately if ever read.
> Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L133-L142)

### Wrap Options and Render Configuration

| Property | Type | Purpose |
| :--- | :--- | :--- |
| `page` | `string` | The route page pathname being wrapped |
| `cacheHandlers` | `Record<string, CacheHandler>` | Optional custom cache handlers bound during edge request initialization |
| `incrementalCacheHandler` | `typeof IncrementalCacheHandler` | Optional incremental cache handler class reference passed to the edge adapter |

Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L24-L28)

## Default In-Memory Cache Store Implementation

### Overview

The default in-memory cache handler provides local, LRU-backed storage for cached function outputs and React Flight streams. When configured with a maximum memory size (`maxSize`), it instantiates an internal `LRUCache` instance that sizes entries based on the byte length of their underlying `ReadableStream` chunks. If `maxSize` is set to `0`, the handler short-circuits to bypass memory allocation entirely, returning resolved `undefined` or no-op promises for all operations.
Sources: [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L44-L61)

### Pending Promise Resolution and In-Flight Coalescing

To prevent duplicate concurrent execution during cache population, the default cache handler tracks active asynchronous writes using a `pendingSets` map keyed by `cacheKey`. When a `set` operation begins, it generates a new `Promise` and stores its resolver in `pendingSets`. Incoming `get` requests check this map and await the pending promise before querying the underlying LRU store, ensuring that concurrent requests coalesce onto a single active write operation.
Sources: [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L62-L75), [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L130-L138)

### Expiration and Tag Tracking Mechanics

Cache entry validity is governed by timestamps, age thresholds, and associated cache tags managed via the tags manifest. The `get` implementation evaluates whether an entry has expired based on environment runtime targets: production environments drop entries once they pass their `revalidate` window, whereas development servers (`next dev`) serve stale entries until their absolute `expire` time is reached, relying on stale-while-revalidate wrappers to trigger background refreshing.
Sources: [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L1-L13), [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L86-L99)

> [!NOTE]
> Tag validation inspects both `areTagsExpired` and `areTagsStale`. If any associated tag is expired, the cache entry is treated as a cache miss (`undefined`), whereas stale tags downgrade the effective revalidate value to `-1` to signal stale-while-revalidate behavior.
> Sources: [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L103-L111)

### Cache Handler Interface Implementation

| Method | Parameters | Return Type | Purpose |
| :--- | :--- | :--- | :--- |
| `get` | `cacheKey: string` | `Promise<CacheEntry \| undefined>` | Retrieves a cached entry if present, valid, and unexpired, teeing its stream value. |
| `set` | `cacheKey: string, pendingEntry: Promise<CacheEntry>` | `Promise<void>` | Awaits entry resolution, calculates stream byte size, and stores it in the LRU cache. |
| `refreshTags` | `tags: string[], soft?: boolean` | `Promise<void>` | No-op method for in-memory cache handlers. |
| `getExpiration` | `tags: string[]` | `Promise<number>` | Calculates the most recent expiration timestamp across the provided tags manifest entries. |
| `updateTags` | `tags: string[], durations?: { expire?: number }` | `Promise<void>` | Marks specified tags as stale and updates expiration durations in the tags manifest. |

Sources: [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L69-L213)

## Development Probe Pool and Worker Scheduling

### Overview

During development, Next.js implements a hang-detection probe pool and worker scheduling architecture to diagnose and resolve deadlocks during `'use cache'` execution. When a cache fill operation stalls or hangs beyond a configured threshold, the main server process dispatches a probe request to an isolated background worker thread or child process. This mechanism uses `jest-worker` to maintain a pool of four workers, ensuring that state exploration does not block the primary request-handling thread.
Sources: [packages/next/src/server/dev/use-cache-probe-pool.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-pool.ts#L58-L122), [packages/next/src/server/dev/use-cache-probe-worker.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-worker.ts#L65-L180)

### Probe Worker Initialization and Execution Flow

The execution walkthrough for a hang-detection probe spans the main thread pool and the worker module as follows: `installUseCacheProbe()` wires the global hook in `use-cache-probe-globals.ts` → when a fill stalls, `runProbe()` grabs an active pool via `getPool()` → `activePool.probeUseCache(msg)` is called with a serialized `ProbeMessage` → the worker executes `probeUseCache()` in `use-cache-probe-worker.ts`, which sets HTTP agent options, loads components via `loadComponents()`, retrieves the server module map via `getServerModuleMap()`, decodes arguments using `decodeReply()` or `decodeReplyFromAsyncIterable()`, builds a request store via `buildProbeWorkStore()`, and finally runs the wrapped `'use cache'` function inside `workAsyncStorage.run()` and `workUnitAsyncStorage.run()`.
Sources: [packages/next/src/server/dev/use-cache-probe-pool.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-pool.ts#L63-L194), [packages/next/src/server/dev/use-cache-probe-worker.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-worker.ts#L65-L180)

> [!NOTE]
> The probe worker runs without an outer render context. Because of this isolation, cache-scope fetches resolve normally, and the shared module scope can never accumulate a halted promise that would poison sibling probe tasks.
> Sources: [packages/next/src/server/dev/use-cache-probe-pool.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-pool.ts#L66-L72)

### Probe Configuration and Worker Options

| Option / Parameter | Type | Meaning / Purpose |
| :--- | :--- | :--- |
| `maxRetries` | `number` | Set to `0` to prevent automatic worker retries upon failure. |
| `numWorkers` | `number` | Fixed at `4` concurrent workers to absorb parallel cache fill probes. |
| `enableWorkerThreads` | `boolean` | Controlled by `nextConfig.experimental.workerThreads` to toggle worker threads versus child processes. |
| `exposedMethods` | `string[]` | Explicitly lists `['probeUseCache']` to bypass parent-process discovery overhead. |
| `timeoutMs` | `number` | Configured timeout threshold triggering the deadlock probe. |

Sources: [packages/next/src/server/dev/use-cache-probe-pool.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-pool.ts#L99-L121)

> [!WARNING]
> The dev server drops and tears down the entire worker pool whenever `onCacheInvalidation()` fires (such as during HMR refreshes or route recompilations). Without this teardown, workers would retain stale `require.cache` and manifest bindings from previous code versions.
> Sources: [packages/next/src/server/dev/use-cache-probe-worker.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-worker.ts#L71-L79), [packages/next/src/server/dev/use-cache-probe-pool.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-pool.ts#L159-L166)

### Design Trade-Offs

| Design Choice | Benefit | Cost |
| :--- | :--- | :--- |
| **Isolated `jest-worker` pool** | Prevents deadlocked cache fills from freezing the main dev server request thread. | Fixed worker memory footprint and serialization overhead for arguments. |
| **Base64 blob encoding** | Ensures binary payload survival across child-process JSON fallback transports and worker threads. | Additional CPU overhead and memory allocation during argument serialization. |
| **Coarse pool teardown on HMR** | Guarantees absolute freshness of user modules and module manifests without path-level tracking. | Discards warm worker caches on every file invalidation event. |

Sources: [packages/next/src/server/dev/use-cache-probe-worker.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-worker.ts#L38-L44), [packages/next/src/server/dev/use-cache-probe-pool.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-pool.ts#L66-L77), [packages/next/src/server/dev/use-cache-probe-pool.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-pool.ts#L159-L166)

## Legacy Cache Interoperability and Fetch Patching

### Overview

The caching infrastructure interoperates with legacy primitives such as `unstable_cache`, patched native `fetch` operations, and coalesced function invocations. These mechanisms interact through unified incremental cache layers, shared asynchronous storage boundaries, and request stores.
Sources: [packages/next/src/server/web/spec-extension/unstable-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts#L100-L135), [packages/next/src/lib/coalesced-function.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/coalesced-function.ts#L10-L41)

### `unstable_cache` Execution and Revalidation

The `unstable_cache` wrapper intercepts expensive operations by combining a fixed function key with serialized arguments, fetching from or updating the underlying `IncrementalCache`.
Sources: [packages/next/src/server/web/spec-extension/unstable-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts#L61-L138)

```typescript
export function unstable_cache<T extends Callback>(
  cb: T,
  keyParts?: string[],
  options: {
    revalidate?: number | false
    tags?: string[]
  } = {}
): T
```
Sources: [packages/next/src/server/web/spec-extension/unstable-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts#L61-L71)

During execution, `unstable_cache` performs the following call-chain sequence:
1. `workAsyncStorage.getStore()` and `workUnitAsyncStorage.getStore()` are queried to retrieve the active request and work unit contexts.
Sources: [packages/next/src/server/web/spec-extension/unstable-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts#L100-L103)
2. `incrementalCache.generateCacheKey(invocationKey)` builds the final hashed key from the fixed key and argument string.
Sources: [packages/next/src/server/web/spec-extension/unstable-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts#L134-L135)
3. If an App Router store is present, `workStore.incrementalCache` or `globalThis.__incrementalCache` is consulted to retrieve stored entries or schedule background revalidations via `workStore.pendingRevalidates[invocationKey]`.
Sources: [packages/next/src/server/web/spec-extension/unstable-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts#L105-L115), [packages/next/src/server/web/spec-extension/unstable-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts#L284-L296)
4. If the cache entry is missing or invalid, `workUnitAsyncStorage.run(innerCacheStore, cb, ...args)` runs the underlying callback within an `unstable-cache` work unit store, persisting the result using `cacheNewResult()`.
Sources: [packages/next/src/server/web/spec-extension/unstable-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts#L143-L153), [packages/next/src/server/web/spec-extension/unstable-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts#L307-L331)

> [!WARNING]
> Passing `revalidate: 0` to `unstable_cache()` throws an invariant error; revalidation must be either `false` or a positive number greater than zero.
> Sources: [packages/next/src/server/web/spec-extension/unstable-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts#L72-L76)

### Fetch Patching and Response Caching

Patched fetchers integrate with work stores and incremental caches to store network response payloads as cached fetch data entries.
Sources: [packages/next/src/server/lib/patch-fetch.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/patch-fetch.ts#L164-L170), [packages/next/src/server/lib/patch-fetch.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/patch-fetch.ts#L262-L265)

```typescript
export function createPatchedFetcher(
  originFetch: Fetcher,
  { workAsyncStorage, workUnitAsyncStorage }: PatchableModule
): PatchedFetcher
```
Sources: [packages/next/src/server/lib/patch-fetch.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/patch-fetch.ts#L262-L265)

During dynamic rendering, `createCachedDynamicResponse` clones response streams, converts array buffers into base64-encoded body strings, and associates them with `serverComponentsHmrCache` and incremental cache instances while deduplicating simultaneous set operations through `workStore.pendingRevalidates`.
Sources: [packages/next/src/server/lib/patch-fetch.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/patch-fetch.ts#L182-L254)

### Coalesced Function Invocations

Concurrent identical function executions are deduplicated using `withCoalescedInvoke`, which maintains a global in-memory promise map.
Sources: [packages/next/src/lib/coalesced-function.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/coalesced-function.ts#L8-L41)

```typescript
export function withCoalescedInvoke<F extends (...args: any) => any>(
  func: F
): (
  key: string,
  args: Parameters<F>
) => Promise<CoalescedInvoke<UnwrapPromise<ReturnType<F>>>>
```
Sources: [packages/next/src/lib/coalesced-function.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/coalesced-function.ts#L10-L15)

When an invocation occurs, `withCoalescedInvoke` checks `globalInvokeCache` for an existing promise mapped to the key. If an entry exists, subsequent callers receive a cloned promise resolving with `isOrigin: false`. If no entry exists, a wrapper executes `func.apply(undefined, args)`, registers the pending promise in `globalInvokeCache`, and deletes the key upon settlement or rejection.
Sources: [packages/next/src/lib/coalesced-function.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/coalesced-function.ts#L16-L40)

> [!NOTE]
> `withCoalescedInvoke` cleans up its internal tracking map immediately upon promise resolution or rejection in both `.then()` and `.catch()` blocks, preventing permanent memory leaks for transient operations.
> Sources: [packages/next/src/lib/coalesced-function.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/coalesced-function.ts#L30-L37)

## Related

- [Incremental Cache](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/caching-and-export/incremental-cache)


## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/llms.txt) for all pages in this wiki.
