Custom Incremental Cache Handler with Redis #52203
|
We are running Next.js 13+ in Kubernetes with 2+ pods. However, we have encountered an issue with file storage caching that leads to a revalidateTag issue. The tags are not always validated because the API route only validates the filesystem where the load balancer points to. To achieve our desired outcome, we are attempting to implement Redis so that Next.js can cache fetch results into a Redis key-value store in a shared pod. According to Next.js's documentation, the incrementalCacheHandlerPath accomplishes exactly this, but the documentation on that particular topic is insufficient. I have created my own cache handler, but when I look at the file-system-cache.ts file of Next.js, I see that it also handles other caches like ROUTE and IMAGE, which are cache types I am unfamiliar with. I am unsure how to proceed. Could you please review the code below and provide some guidance? It is currently functioning, but I am uncertain about its stability since it lacks support for other cache types. My other question is are the cache keys same between pods for the same fetch requests? const Redis = require("ioredis");
const {getDerivedTags} = require("next/dist/server/lib/incremental-cache/utils");
const redis = new Redis();
const TAG_MANIFESTS_KEY = 'tags-manifest'
module.exports = class CacheHandler {
revalidatedTags = []
constructor(options) {
this.options = options
this.revalidatedTags = options.revalidatedTags
}
async loadTagsManifest() {
const tagManifest = await redis.get(TAG_MANIFESTS_KEY)
if(!tagManifest) {
return {version: 1, items: {}}
}
try {
return JSON.parse(tagManifest)
} catch (err) {
console.log(err)
return {version: 1, items: {}}
}
}
async setTags(key, tags) {
const tagsManifest = await this.loadTagsManifest()
if (!tagsManifest) {
return null
}
for (const tag of tags) {
const data = tagsManifest.items[tag] || {keys: []}
if (!data.keys.includes(key)) {
data.keys.push(key)
}
tagsManifest.items[tag] = data
}
try {
await redis.set(TAG_MANIFESTS_KEY, JSON.stringify(tagsManifest || {}))
} catch (err) {
console.warn('Failed to update tags manifest.', err)
}
}
async get(key) {
let data = JSON.parse(await redis.get(key))
if(data) {
const tagsManifest = await this.loadTagsManifest()
const innerData = data.value.data
const derivedTags = getDerivedTags(innerData.tags || [])
const wasRevalidated = derivedTags.some((tag) => {
if (this.revalidatedTags.includes(tag)) {
return true
}
return (
tagsManifest?.items[tag]?.revalidatedAt &&
tagsManifest?.items[tag].revalidatedAt >=
(data?.lastModified || Date.now())
)
})
// When revalidate tag is called we don't return
// stale data, so it's updated right away
if (wasRevalidated) {
data = undefined
}
}
return data || null
}
async set(key, data) {
await redis.set(key, JSON.stringify(
{
value: data,
lastModified: Date.now(),
}
))
await this.setTags(key, data.data.tags || [])
}
async revalidateTag(tag) {
const tagsManifest = await this.loadTagsManifest()
if (!tagsManifest) {
return null
}
const data = tagsManifest.items[tag] || { keys: [] }
data.revalidatedAt = Date.now()
tagsManifest.items[tag] = data
try {
await redis.set(TAG_MANIFESTS_KEY, JSON.stringify(tagsManifest || {}))
} catch (err) {
console.warn('Failed to update tags manifest.', err)
}
}
} |
Replies: 3 comments 5 replies
|
any information about it, everyone? |
|
Hi also ran into the file cache problem. Only in my case the file cache grows up to 30GB in a week, because there is no system to clear it, only rebuild with removing .next folder. I also looked at file-system-cache.js. to try and make my own implementation.
There really is no PAGE cache processing in your solution. You need to store tags and keys for the PAGE in the manifest, and when you get the cache, retrieve the tags and check if they are out of date. This is exactly the same as it is done in the original file. Also make sure that |
|
I am also looking for answers here vercel/vercel#10972 Facing issue with the website https://dsabyte.com which has been deployed on vercel and |
Next now has examples for using a custom Redis cache handler
@neshca/cache-handler