Created
January 6, 2025 09:14
-
-
Save H01001000/1091709d6109211488fd6ea2dc6e0ee6 to your computer and use it in GitHub Desktop.
This patch allow using redis or keyDB for caching next.js's image optimizer cache, for yarn on next 15.1.2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/dist/server/image-optimizer.js b/dist/server/image-optimizer.js | |
index bf6beaeeffab903e0b02677fd633729df20f27ed..ce0549fbd0b21d7f4c610958e9eb1bcd0b5d79f8 100644 | |
--- a/dist/server/image-optimizer.js | |
+++ b/dist/server/image-optimizer.js | |
@@ -466,33 +466,57 @@ class ImageOptimizerCache { | |
mimeType | |
]); | |
} | |
- constructor({ distDir, nextConfig }){ | |
+ constructor({ distDir, nextConfig, redisClient }){ | |
this.cacheDir = (0, _path.join)(distDir, 'cache', 'images'); | |
this.nextConfig = nextConfig; | |
+ this.redisClient = redisClient; | |
} | |
async get(cacheKey) { | |
try { | |
- const cacheDir = (0, _path.join)(this.cacheDir, cacheKey); | |
- const files = await _fs.promises.readdir(cacheDir); | |
- const now = Date.now(); | |
- for (const file of files){ | |
- const [maxAgeSt, expireAtSt, etag, upstreamEtag, extension] = file.split('.', 5); | |
- const buffer = await _fs.promises.readFile((0, _path.join)(cacheDir, file)); | |
+ if (process.env.REDIS_IMAGE_CACHE === 'true') { | |
+ const file = await this.redisClient.hgetall(cacheKey) | |
+ if (Object.keys(file).length === 0) return null | |
+ | |
+ const now = Date.now(); | |
+ const {maxAge: maxAgeSt, expireAt: expireAtSt, etag, upstreamEtag, extension, buffer} = file; | |
const expireAt = Number(expireAtSt); | |
const maxAge = Number(maxAgeSt); | |
return { | |
value: { | |
kind: _responsecache.CachedRouteKind.IMAGE, | |
- etag, | |
- buffer, | |
- extension, | |
- upstreamEtag | |
+ etag, | |
+ buffer: Buffer.from(buffer, "base64"), | |
+ extension, | |
+ upstreamEtag | |
}, | |
revalidateAfter: Math.max(maxAge, this.nextConfig.images.minimumCacheTTL) * 1000 + Date.now(), | |
curRevalidate: maxAge, | |
isStale: now > expireAt, | |
isFallback: false | |
}; | |
+ } else { | |
+ const cacheDir = (0, _path.join)(this.cacheDir, cacheKey); | |
+ const files = await _fs.promises.readdir(cacheDir); | |
+ const now = Date.now(); | |
+ for (const file of files){ | |
+ const [maxAgeSt, expireAtSt, etag, upstreamEtag, extension] = file.split('.', 5); | |
+ const buffer = await _fs.promises.readFile((0, _path.join)(cacheDir, file)); | |
+ const expireAt = Number(expireAtSt); | |
+ const maxAge = Number(maxAgeSt); | |
+ return { | |
+ value: { | |
+ kind: _responsecache.CachedRouteKind.IMAGE, | |
+ etag, | |
+ buffer, | |
+ extension, | |
+ upstreamEtag | |
+ }, | |
+ revalidateAfter: Math.max(maxAge, this.nextConfig.images.minimumCacheTTL) * 1000 + Date.now(), | |
+ curRevalidate: maxAge, | |
+ isStale: now > expireAt, | |
+ isFallback: false | |
+ }; | |
+ } | |
} | |
} catch (_) { | |
// failed to read from cache dir, treat as cache miss | |
@@ -508,7 +532,18 @@ class ImageOptimizerCache { | |
} | |
const expireAt = Math.max(revalidate, this.nextConfig.images.minimumCacheTTL) * 1000 + Date.now(); | |
try { | |
- await writeToCacheDir((0, _path.join)(this.cacheDir, cacheKey), value.extension, revalidate, expireAt, value.buffer, value.etag, value.upstreamEtag); | |
+ if (process.env.REDIS_IMAGE_CACHE === 'true') { | |
+ await this.redisClient.hmset(cacheKey, { | |
+ extension: value.extension, | |
+ maxAge: revalidate, | |
+ expireAt, | |
+ buffer: value.buffer.toString("base64"), | |
+ etag: value.etag, | |
+ upstreamEtag: value.upstreamEtag | |
+ }) | |
+ } else { | |
+ await writeToCacheDir((0, _path.join)(this.cacheDir, cacheKey), value.extension, revalidate, expireAt, value.buffer, value.etag, value.upstreamEtag); | |
+ } | |
} catch (err) { | |
_log.error(`Failed to write image to cache ${cacheKey}`, err); | |
} | |
diff --git a/dist/server/next-server.js b/dist/server/next-server.js | |
index 68b9639d21c369a1d574ed548b7953c188c1ab97..1e0542ca207b97fd500fcb4880934931f399a08a 100644 | |
--- a/dist/server/next-server.js | |
+++ b/dist/server/next-server.js | |
@@ -62,6 +62,7 @@ const _routekind = require("./route-kind"); | |
const _invarianterror = require("../shared/lib/invariant-error"); | |
const _awaiter = require("./after/awaiter"); | |
const _asynccallbackset = require("./lib/async-callback-set"); | |
+const Redis = require("ioredis"); | |
function _export_star(from, to) { | |
Object.keys(from).forEach(function(k) { | |
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) { | |
@@ -154,7 +155,8 @@ class NextNodeServer extends _baseserver.default { | |
const { ImageOptimizerCache } = require('./image-optimizer'); | |
const imageOptimizerCache = new ImageOptimizerCache({ | |
distDir: this.distDir, | |
- nextConfig: this.nextConfig | |
+ nextConfig: this.nextConfig, | |
+ redisClient: this.redisClient | |
}); | |
const { sendResponse, ImageError } = require('./image-optimizer'); | |
if (!this.imageResponseCache) { | |
@@ -373,6 +375,11 @@ class NextNodeServer extends _baseserver.default { | |
} | |
return result.finished; | |
}; | |
+ this.redisClient = process.env.REDIS_IMAGE_CACHE ? new Redis({ | |
+ host: process.env.REDIS_HOST, | |
+ port: parseInt(process.env.REDIS_PORT ?? "6379"), | |
+ password: process.env.REDIS_PASSWORD, | |
+ }) : null; | |
/** | |
* This sets environment variable to be used at the time of SSR by head.tsx. | |
* Using this from process.env allows targeting SSR by calling |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Backstory: Im selfhosting Next.js on multiple vm on cloud, those vm are very small and takes a bit time and cpu for rendering the image.
By using this patch + keyDB multi master replication, Im able to sync the image cache, and restore after update the app
requirement:
only tested on next 15.1.2
install:
.yarn/patches
folderpatch:next@npm%3A15.1.2#~/.yarn/patches/next-npm-15.1.2-24e7411703.patch
in package.jsonusage:
add follow env