Dev & EngARTICLE

Vercel merges Data Cache and Runtime Cache without requiring code changes

Vercel merges Data Cache and Runtime Cache without requiring code changes
Image: Carina Ferreira
Article · Dev & Eng
Reading
6 min
Tables
1
In short

The company is bringing Data Cache inside Runtime Cache, unifying four caching APIs under the same infrastructure. Developers using fetch or unstable_cache in Next.js don't need to touch their code, but the storage and billing model changes underneath.

Vercel announced, in a post on its official blog, that it is consolidating two caching products that until now lived separately: Data Cache, used since the launch of Next.js's App Router to power cached fetch and unstable_cache, and Runtime Cache, introduced last year as a generic API (getCache, from the @vercel/functions package) for any framework. From now on, the four entry points (cached fetch, unstable_cache, getCache, and Next.js's 'use cache: remote' directive) will use the same storage infrastructure.

For those who already have code in production, Vercel's promise is clear: nothing changes on the surface. If the project uses fetch with Next.js's cache option or calls unstable_cache, the observable behavior stays the same. What changes is what's behind the call, and that has a direct effect on storage budget, billing, and the observability tools the team uses to investigate caching behavior.

Why there were two products

Data Cache was born tied to the App Router: it stores the data the application uses to build a response, not the full HTTP response. It's the difference between caching a product catalog's payload and caching the entire already-rendered page (that's the job of CDN caching, another Vercel product). A product page, for example, can reuse catalog data from the cache while still personalizing the final response for each visitor.

The problem is that this architecture only made sense within Next.js. Vercel wanted to offer the same service to those using SvelteKit, Nuxt, or any other stack running on the platform, hence Runtime Cache, with a direct API and no framework coupling. Today it's already used for feature flag lookups in Routing Middleware, for caching CMS content during build, and, within Next.js itself, to power 'use cache: remote'.

How Runtime Cache works under the hood

Vercel detailed the engineering behind the product, and the numbers give a sense of scale: a Go API, backed by Redis fleets in each region of the platform, processes more than 440 million reads per hour and moves more than 18 TB of data in the same period.

The design is regional and simple on the surface:

  • Each region has its own cache; a cache hit costs one round trip to the service in the region where the code runs.
  • On a cache miss, the application fetches or computes the value and writes it to the local region, without checking other regions.
  • Authentication uses short-lived credentials generated by Vercel itself, so there's no API key to manage.
  • Each entry stores its tags and write time; invalidating a tag writes a single timestamp, even if a million entries share that tag.

This last point is what makes tag-based invalidation cheap: instead of locating and deleting entries one by one, the service just compares each entry's write timestamp with the invalidation timestamp of the corresponding tag, stored in a separate tag store in the same region. If the tag was invalidated after the entry was written, it's considered stale. Propagating that timestamp to other regions uses the same global push infrastructure that Vercel's CDN already uses, and it typically takes a few hundred milliseconds to reach all regions.

What Vercel already optimized before announcing the merger

Before merging the two products, Vercel spent the last year tuning Runtime Cache, and it's worth understanding these changes because they apply to every cache that migrates there.

Reads under traffic bursts. Checking whether an entry is still fresh adds a dependency to every cache hit: if a tag store shard gets slow, it delays reads even when the cached value was already available. Vercel implemented circuit breakers that let reads use freshness timestamps kept in memory when the shard is slow, avoiding the wait. The side effect is that, during this fallback, a read can treat as fresh a piece of data that was invalidated just moments earlier.

These breakers were enabled in all regions in August. According to Vercel, during the heaviest bursts in the platform's largest region, the cache service's p99 latency dropped to about a fifth of its previous level under comparable traffic, and on-call alerts for this type of failure stopped firing.

More space, fewer evictions. Runtime Cache storage budgets increased for Pro projects in June and for Enterprise in October. Among Enterprise projects that suffered frequent evictions, the cache miss rate dropped by half within a day, according to data published by the company.

More visibility. The Runtime Cache tab in Vercel's Observability gained deploy markers, revalidation causes, and write utilization, useful for comparing changes in cache behavior with releases and identifying data that gets written to the cache but is rarely read.

What changes (and what doesn't) in practice

The table summarizes the before and after for those running a Next.js stack in production on Vercel:

AspectBeforeAfter the merger
fetch with cache / unstable_cacheUsed Data CacheNow use Runtime Cache, no code changes
getCache / 'use cache: remote'Already used Runtime CacheStay the same
Storage (Pro)Pool shared across the whole teamOwn allocation per project and environment, 8x larger
Storage (Enterprise)Data Cache pool10x the previous space
BillingData Cache reads and writes weren't billedNow follow Runtime Cache pricing

The point that deserves attention from whoever manages billing is the last row: Data Cache reads and writes that weren't previously billed now follow the Runtime Cache pricing model. Vercel says existing teams will receive migration and billing details in advance, and that new teams are already starting out using Runtime Cache by default.

Where this matters for caching strategy

For a team that already uses unstable_cache with well-defined tags, the migration is essentially transparent, and the real gain is the increased budget: a storage pool per project and environment (instead of a pool shared across the whole team) tends to reduce the most common pain scenario in production, which is the application losing cached data due to lack of space right at the peak of traffic.

It's worth reading the optimization guide Vercel published alongside the changelog, though, before assuming the larger budget solves everything: if the application writes to the cache more than it reads from it, the problem isn't pool size, it's tag and revalidation strategy. The new write utilization tab in Observability exists precisely to expose this kind of waste, which went unnoticed when Data Cache didn't charge for writes.

There's also an architectural point worth noting: the circuit breaker fallback, which improves p99 latency during bursts, has the trade-off of briefly accepting data as fresh even after it's been invalidated. For most applications this is harmless, but teams with strong consistency requirements after a tag invalidation (for example, a pricing dashboard that can't show a stale value even for a few milliseconds) should factor in this behavior when deciding which data to place behind shared tags.

Translated from the Brazilian Portuguese original · Read the original