feat: add Redis cache components handler for "use cache" support#207
feat: add Redis cache components handler for "use cache" support#207nathanschram wants to merge 3 commits intofortedigital:feature/cache-componentsfrom
Conversation
Implements CacheComponentsHandler interface using Redis strings, enabling distributed caching for Next.js 16+ "use cache" directive. - Stores RSC Flight payloads as base64-encoded strings in Redis - Tag-based invalidation via Redis hash maps - Timeout protection on all Redis operations via AbortSignal - Supports Redis Cluster via existing adapter - Updates example project to use the new handler Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
25 tests covering get/set/refreshTags/getExpiration/updateTags: - Cache miss/hit, expiry, tag revalidation, soft tags, keyPrefix - Base64 stream encoding, pending entry await, tag storage - Cursor pagination in updateTags, selective key deletion Also formats cache-components-handler.ts and cache-handler.types.ts to match project prettier config. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Awesome! 🚀🚀🚀 I will give it a try and review, hopefully this week. |
|
I’ve tested the implementation at a high level (without a deep dive into the code) and wanted to share a few general observations and suggestions. Points to consider:
@nathanschram thanks a bunch! Overall, the basic tests indicate that the solution works as expected. I’m comfortable proceeding with it (pending a more in-depth code review) and iterating from there. That said, if you’d like to address any of the above points now, I’d definitely appreciate it. I will dive deeper into the code and in the meantime I look forward to a follow up from you, cheers! |
The example now checks PHASE_PRODUCTION_BUILD before connecting to Redis, matching the pattern used by the existing redis-minimal example. This prevents build failures when Redis is not available during `next build`. Also migrates from .js (CJS) to .mjs (ESM) for consistency with the existing standard handler example. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Thanks for testing this out and for the detailed feedback - really helpful. I've pushed a fix for point 1 and dug into the rest. Here's where I landed on each: 1. Build-time init - good catch. I've updated the example to check 2. In-memory caching after purge - I think this is Next.js's built-in LRU cache layer rather than something from our handler. Next.js runs a 3. 4. 5. Extensibility - agreed this could be more composable. I deliberately matched the existing Let me know if you'd like me to adjust anything else or if there are specific things to look at more closely. |
|
@nathanschram I cannot build the example project after the latest changes. ( It just hangs and times out at this step
|
| // Only connect to Redis outside of `next build` — Redis is not available | ||
| // during the build phase. The handler checks client.isReady on each method | ||
| // call, so an unconnected client at build time is safe. | ||
| if (process.env.NEXT_PHASE !== PHASE_PRODUCTION_BUILD) { | ||
| client.connect().catch((err) => { | ||
| console.warn("Redis connection failed:", err.message); | ||
| }); | ||
| } |
There was a problem hiding this comment.
This does not really solve the problem of handler saving to redis at built time. It only causes failures, the cache handler is still registered, still calls redis methods.
There was a problem hiding this comment.
And this is exactly why the npm run build does not work.
There was a problem hiding this comment.
Please check how PHASE_PRODUCTION_BUILD is used in cache-handler.ts to properly implement skipping redis calls at built time.
|
Hey - reproduced the hang locally. The root cause is that I've got a fix ready matching the One question before I push: I also made the handler methods themselves degrade gracefully when the client isn't ready (returning |
Good idea with the graceful error handling, let's do it. |


Closes #152
Adds a Redis-backed
CacheComponentsHandlerimplementation for Next.js 16's"use cache"directive, following the existingredis-stringshandler patterns.What this does:
redis-strings-cache-componentshandler implementing theCacheComponentsHandlerinterfaceReadableStream<Uint8Array>) as base64-encoded strings in Redis__cc_sharedTags__,__cc_revalidatedTags__)AbortSignal(same as existing handler)examples/redis-cache-components) to use the new handlerUsage:
Design notes:
__cc_*prefix) to avoid collisions with the existing handlerrefreshTags()is a no-op since revalidation state is queried directly from Redis on eachget()/getExpiration()callupdateTags()currently does immediate invalidation (ignoresdurationsparameter) - could add gradual expiration support later if neededHappy to adjust the approach or add anything that's missing.