From 41910802f2a5d66a402cb83de144f119df76fca2 Mon Sep 17 00:00:00 2001 From: Rongrong <15956627+Rongronggg9@users.noreply.github.com> Date: Sat, 19 Mar 2022 02:13:15 +0800 Subject: [PATCH] feat(core): caching enhancements (#9227) * fix(core): redis cache ignore `maxAge` when refreshing memory cache do not have such a problem Signed-off-by: Rongrong <15956627+Rongronggg9@users.noreply.github.com> * feat(core): make `cache.tryGet()` refreshing selectable Signed-off-by: Rongrong <15956627+Rongronggg9@users.noreply.github.com> * docs: `ctx.cache.tryGet()` update Signed-off-by: Rongrong <15956627+Rongronggg9@users.noreply.github.com> --- docs/en/joinus/quick-start.md | 2 +- docs/joinus/quick-start.md | 2 +- lib/middleware/cache/index.js | 4 ++-- lib/middleware/cache/redis.js | 19 +++++++++++++++++-- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/docs/en/joinus/quick-start.md b/docs/en/joinus/quick-start.md index 671503882..17b40dde6 100644 --- a/docs/en/joinus/quick-start.md +++ b/docs/en/joinus/quick-start.md @@ -291,7 +291,7 @@ const description = await ctx.cache.tryGet(link, async () => { }); ``` -The implementation of tryGet can be seen [here](https://github.com/DIYgod/RSSHub/blob/master/lib/middleware/cache/index.js#L58). The first parameter is the cache key, the second parameter is the cache data acquisition method, and the third parameter is the cache time, it should not be passed in normally. The cache time defaults to [CACHE_CONTENT_EXPIRE](/en/install/#cache-configurations), and each time accessing the cache will recalculate the expiration time +The implementation of tryGet can be seen [here](https://github.com/DIYgod/RSSHub/blob/master/lib/middleware/cache/index.js#L58). The 1st parameter is the cache key; the 2nd parameter is the cache data acquisition method (executed when cache miss); the 3rd parameter is the cache time, it should not be passed in normally and defaults to [CACHE_CONTENT_EXPIRE](/en/install/#cache-configurations); the 4th parameter determines whether to recalculate the expiration time ("renew" the cache) when the current attempt cache hits, `true` is on, `false` is off, default is on --- diff --git a/docs/joinus/quick-start.md b/docs/joinus/quick-start.md index c45014879..51a4eda9e 100644 --- a/docs/joinus/quick-start.md +++ b/docs/joinus/quick-start.md @@ -292,7 +292,7 @@ const description = await ctx.cache.tryGet(link, async () => { }); ``` -tryGet 的实现可以看[这里](https://github.com/DIYgod/RSSHub/blob/master/lib/middleware/cache/index.js#L58),第一个参数为缓存的 key,第二个参数为缓存数据获取方法,第三个参数为缓存时间,正常情况不应该传入,缓存时间默认为 [CACHE_CONTENT_EXPIRE](/install/#缓存配置),且每次访问缓存会重新计算过期时间 +tryGet 的实现可以看[这里](https://github.com/DIYgod/RSSHub/blob/master/lib/middleware/cache/index.js#L58)。第一个参数为缓存的 key;第二个参数为缓存未命中时的数据获取方法;第三个参数为缓存时间,正常情况不应该传入,缓存时间默认为 [CACHE_CONTENT_EXPIRE](/install/#缓存配置);第四个参数为控制本次尝试缓存命中时是否需要重新计算过期时间(给缓存「续期」)的开关,`true` 为打开,`false` 为关闭,默认为打开 * * * diff --git a/lib/middleware/cache/index.js b/lib/middleware/cache/index.js index 7e6bb926e..178f700d6 100644 --- a/lib/middleware/cache/index.js +++ b/lib/middleware/cache/index.js @@ -55,8 +55,8 @@ module.exports = function (app) { const { get, set, status } = cacheModule; app.context.cache = { ...cacheModule, - tryGet: async (key, getValueFunc, maxAge = config.cache.contentExpire) => { - let v = await get(key); + tryGet: async (key, getValueFunc, maxAge = config.cache.contentExpire, refresh = true) => { + let v = await get(key, refresh); if (!v) { v = await getValueFunc(); set(key, v, maxAge); diff --git a/lib/middleware/cache/redis.js b/lib/middleware/cache/redis.js index d461bc87c..eb534abca 100644 --- a/lib/middleware/cache/redis.js +++ b/lib/middleware/cache/redis.js @@ -18,12 +18,26 @@ redisClient.on('connect', () => { logger.info('Redis connected.'); }); +const getCacheTtlKey = (key) => { + if (key.startsWith('cacheTtl:')) { + throw Error('"cacheTtl:" prefix is reserved for the internal usage, please change your cache key'); // blocking any attempt to get/set the cacheTtl + } + return `cacheTtl:${key}`; +}; + module.exports = { get: async (key, refresh = true) => { if (key && status.available) { - let value = await redisClient.get(key); + const cacheTtlKey = getCacheTtlKey(key); + let [value, cacheTtl] = await redisClient.mget(key, cacheTtlKey); if (value && refresh) { - redisClient.expire(key, config.cache.contentExpire); + if (!cacheTtl) { + cacheTtl = config.cache.contentExpire; + redisClient.set(cacheTtlKey, cacheTtl, 'EX', cacheTtl); + } else { + redisClient.expire(cacheTtlKey, cacheTtl); + } + redisClient.expire(key, cacheTtl); value = value + ''; } return value; @@ -40,6 +54,7 @@ module.exports = { value = JSON.stringify(value); } if (key) { + redisClient.set(getCacheTtlKey(key), maxAge, 'EX', maxAge); return redisClient.set(key, value, 'EX', maxAge); // setMode: https://redis.io/commands/set } },