Skip to main content
Why CacheeHow It Works
All Verticals5G TelecomAd TechAI InfrastructureFraud DetectionGamingTrading
PricingDocsBlogSchedule DemoLog InStart Free Trial
← Back to Blog
Engineering

Cache Fusion: Stop Invalidating Entire Responses Because One Field Changed

Your API returns a user profile: name, preferences, orders, billing. You cache the whole response. The user updates their display name. The entire cache entry is gone — orders, preferences, billing, all of it. You recompute the full response from scratch because one field changed. This is monolithic caching, and it is how every production cache works today. It is also why your hit rates are stuck at 65%.

The Over-Invalidation Problem

Over-invalidation is the most common and least discussed failure mode in caching. It happens when you evict more data than actually changed. With monolithic caching, this is not a bug — it is the only option. The cache stores the entire response as a single entry. There is no way to say “invalidate the name but keep the orders.” The cache has no concept of what a “name” is. It sees bytes.

The consequences compound. A SaaS dashboard with 6 widgets — revenue, active users, recent activity, usage metrics, alerts, team members — is cached as one blob. The alerts widget updates every 30 seconds. So the entire dashboard is invalidated every 30 seconds. Five widgets that change hourly are recomputed every 30 seconds because one widget changes frequently. The cache exists but barely helps.

GraphQL makes this worse. Every query shape — every unique combination of fields a client requests — produces a different cache key. A mobile client requesting 4 fields and a web client requesting 12 fields get zero cache reuse, even though they share 4 fields of data. The combinatorial explosion of query shapes makes full-response caching for GraphQL effectively impossible. Teams end up caching at the resolver level with manual invalidation logic, which breaks the moment resolvers have dependencies on each other.

Personalized APIs face the same problem. A product page with a shared layout and user-specific recommendations is cached per user. Change the product description and every user’s cached page is invalidated, even though only the shared portion changed. The personalization layer multiplies the over-invalidation by the number of users.

The pattern: One field changes. The cache evicts the entire response. The system recomputes everything from scratch. This happens on every write, across every endpoint, thousands of times per second. It is not a corner case. It is the default behavior of every cache in production.

Cache Fusion: Fragment-Level Caching with Read-Time Composition

The solution is to stop caching responses and start caching fragments. Each piece of a response — the name, the preferences, the orders, the billing data — is stored as an independent cache entry. At read time, the cache composes them into a complete response using a new primitive: FUSE.

# Store fragments independently
SET user:123:name "Jane Doe"
SET user:123:prefs '{"theme":"dark","lang":"en"}'
SET user:123:orders '[{"id":901,"total":49.99}]'
SET user:123:billing '{"plan":"pro","renewal":"2026-04-15"}'

# Compose at read time
FUSE user:123:profile FROM user:123:name user:123:prefs user:123:orders user:123:billing

# Now update the name:
SET user:123:name "Jane Smith"
# Only user:123:name is invalidated
# prefs, orders, billing --> still cached (HIT)
# Next FUSE: 1 fresh fragment + 3 cached = full response

The FUSE command reads each fragment pointer, assembles the composite structure, and returns the result. There is no serialization, no deserialization, no merge logic. It is pointer assembly. The cost is typically under 5 microseconds for a 4-fragment composition, regardless of fragment size. A 50-byte name and a 10KB order history are both a single pointer read.

When user:123:name is updated, only that fragment is invalidated. The orders fragment, the preferences fragment, and the billing fragment remain hot. The next FUSE reads one fresh value and three cached values. Instead of a full cache miss and a complete recomputation, you get a partial miss on the single field that actually changed.

Hit Rates: Monolithic vs Fragment

The hit rate improvement is not incremental. It is structural.

Consider a user profile endpoint with 4 fragments, receiving 10 writes per second distributed across all fields. With monolithic caching, every write invalidates the entire entry. At 10 writes/sec, the monolithic entry is invalidated 10 times per second. Between invalidations, reads hit the cache. Depending on your read-to-write ratio, you land somewhere around 60–70% hit rate.

With fragment caching, each write invalidates only the affected fragment. A write to the name field invalidates user:123:name but leaves the other 3 fragments cached. At any given moment, 3 out of 4 fragments are hot. The effective hit rate across all fragments is 90–95%. The same write volume, the same read volume, but the cache is serving 3x more data from memory instead of recomputing it.

Monolithic: 1 write = full miss
user:123:profile —— MISS (recompute everything)

Fragment: 1 write = 1 fragment miss, rest stay hot
user:123:name —— MISS (fetch name only)
user:123:prefs —— HIT
user:123:orders —— HIT
user:123:billing —— HIT

FUSE —— composed response (~3µs)

Composition with Dependency Graph and CDC

Cache Fusion composes with two other Cachee primitives to create a fully automated invalidation pipeline.

Fragments + Dependency Graph

Fragments can declare dependencies using DEPENDS_ON. A billing fragment might depend on a pricing tier key. When the pricing tier changes, the dependency graph invalidates the billing fragment — and only the billing fragment. The rest of the FUSE composition stays hot. Dependency-aware invalidation at fragment granularity means you get both surgical precision (only the affected piece is evicted) and causal correctness (the graph ensures nothing stale is served).

Fragments + CDC

CDC auto-invalidation watches your database change stream and invalidates the corresponding cache key when a row changes. With fragments, CDC invalidates individual fragments instead of entire responses. The orders table gets a new row. CDC fires. user:123:orders is invalidated. The name, preferences, and billing fragments are untouched. The next FUSE assembles 1 fresh fragment and 3 cached fragments. Zero application code. The database change flows through CDC into the fragment layer and the cache self-heals.

Use Cases for Platform Engineering Teams

The shift: Monolithic caching is a 2015 pattern. It made sense when APIs returned simple key-value lookups. Modern APIs return composite, personalized, multi-source responses. Fragment composition is how they should be cached.

Related Reading

Also Read

Cache Fragments. Compose at Read Time. Zero Over-Invalidation.

Fragment composition. Dependency graphs. CDC auto-invalidation. Cross-instance coherence. The cache primitives your API actually needs.

Start Free Trial Schedule Demo