diff --git a/packages/nocodb/src/cache/RedisCacheMgr.ts b/packages/nocodb/src/cache/RedisCacheMgr.ts index f8eeb9a38f..436ad9188e 100644 --- a/packages/nocodb/src/cache/RedisCacheMgr.ts +++ b/packages/nocodb/src/cache/RedisCacheMgr.ts @@ -5,6 +5,7 @@ import { CacheDelDirection, CacheGetType, CacheListProp, + CacheMetaSplitter, } from '~/utils/globals'; const log = debug('nc:cache'); @@ -76,34 +77,55 @@ export default class RedisCacheMgr extends CacheMgr { } return Promise.resolve(o); } - } catch (e) { - return Promise.resolve(res); - } - return Promise.resolve(res); + } catch (e) {} + const valueHelper = res.split(CacheMetaSplitter); + return Promise.resolve(valueHelper[0]); } else if (type === CacheGetType.TYPE_STRING) { - return await this.client.get(key); + return this.client.get(key).then((res) => { + if (!res) { + return res; + } + const valueHelper = res.split(CacheMetaSplitter); + return valueHelper[0]; + }); } log(`Invalid CacheGetType: ${type}`); return Promise.resolve(false); } // @ts-ignore - async set(key: string, value: any): Promise { + async set(key: string, value: any, skipPrepare = false): Promise { if (typeof value !== 'undefined' && value) { log(`RedisCacheMgr::set: setting key ${key} with value ${value}`); + if (typeof value === 'object') { if (Array.isArray(value) && value.length) { return this.client.sadd(key, value); } - const keyValue = await this.get(key, CacheGetType.TYPE_OBJECT); - if (keyValue) { + + if (!skipPrepare) { + // try to get old key value + const keyValue = await this.get(key, CacheGetType.TYPE_OBJECT); + // prepare new key value value = await this.prepareValue(value, this.getParents(keyValue)); } + return this.client.set( key, JSON.stringify(value, this.getCircularReplacer()), ); } + + if (!skipPrepare) { + // try to get old key value + const keyValue = await this.get(key, CacheGetType.TYPE_OBJECT); + // prepare new key value + value = await this.prepareValue( + value.toString(), + this.getParents(keyValue), + ); + } + return this.client.set(key, value); } else { log(`RedisCacheMgr::set: value is empty for ${key}. Skipping ...`); @@ -248,7 +270,11 @@ export default class RedisCacheMgr extends CacheMgr { } // set Get Key log(`RedisCacheMgr::setList: setting key ${getKey}`); - await this.set(getKey, JSON.stringify(value, this.getCircularReplacer())); + await this.set( + getKey, + JSON.stringify(value, this.getCircularReplacer()), + true, + ); // push Get Key to List listOfGetKeys.push(getKey); } @@ -355,6 +381,7 @@ export default class RedisCacheMgr extends CacheMgr { await this.set( key, JSON.stringify(preparedValue, this.getCircularReplacer()), + true, ); list.push(key); @@ -369,17 +396,36 @@ export default class RedisCacheMgr extends CacheMgr { if (value && typeof value === 'object') { value[CacheListProp] = listKeys; } else if (value && typeof value === 'string') { - const keyHelper = value.split(CacheListProp); - if (listKeys.length) { - value = `${keyHelper[0]}${CacheListProp}${listKeys.join(',')}`; + const metaHelper = value.split(CacheMetaSplitter); + if (metaHelper.length > 1) { + const keyVal = metaHelper[0]; + const keyMeta = metaHelper[1]; + try { + const meta = JSON.parse(keyMeta); + meta[CacheListProp] = listKeys; + value = `${keyVal}${CacheMetaSplitter}${JSON.stringify(meta)}`; + } catch (e) { + console.error( + `RedisCacheMgr::prepareValue: keyValue meta is not JSON`, + keyMeta, + ); + throw new Error( + `RedisCacheMgr::prepareValue: keyValue meta is not JSON`, + ); + } + } else { + const meta = { + [CacheListProp]: listKeys, + }; + value = `${value}${CacheMetaSplitter}${JSON.stringify(meta)}`; } } else if (value) { console.error( - `RedisCacheMgr::prepareListKey: keyValue is not object or string`, + `RedisCacheMgr::prepareValue: keyValue is not object or string`, value, ); throw new Error( - `RedisCacheMgr::prepareListKey: keyValue is not object or string`, + `RedisCacheMgr::prepareValue: keyValue is not object or string`, ); } return value; diff --git a/packages/nocodb/src/cache/RedisMockCacheMgr.ts b/packages/nocodb/src/cache/RedisMockCacheMgr.ts index eb75709024..87a9e6f709 100644 --- a/packages/nocodb/src/cache/RedisMockCacheMgr.ts +++ b/packages/nocodb/src/cache/RedisMockCacheMgr.ts @@ -6,6 +6,7 @@ import { CacheDelDirection, CacheGetType, CacheListProp, + CacheMetaSplitter, } from '~/utils/globals'; const log = debug('nc:cache'); @@ -69,34 +70,55 @@ export default class RedisMockCacheMgr extends CacheMgr { } return Promise.resolve(o); } - } catch (e) { - return Promise.resolve(res); - } - return Promise.resolve(res); + } catch (e) {} + const valueHelper = res.split(CacheMetaSplitter); + return Promise.resolve(valueHelper[0]); } else if (type === CacheGetType.TYPE_STRING) { - return await this.client.get(key); + return this.client.get(key).then((res) => { + if (!res) { + return res; + } + const valueHelper = res.split(CacheMetaSplitter); + return valueHelper[0]; + }); } log(`Invalid CacheGetType: ${type}`); return Promise.resolve(false); } // @ts-ignore - async set(key: string, value: any): Promise { + async set(key: string, value: any, skipPrepare = false): Promise { if (typeof value !== 'undefined' && value) { log(`RedisMockCacheMgr::set: setting key ${key} with value ${value}`); + if (typeof value === 'object') { if (Array.isArray(value) && value.length) { return this.client.sadd(key, value); } - const keyValue = await this.get(key, CacheGetType.TYPE_OBJECT); - if (keyValue) { + + if (!skipPrepare) { + // try to get old key value + const keyValue = await this.get(key, CacheGetType.TYPE_OBJECT); + // prepare new key value value = await this.prepareValue(value, this.getParents(keyValue)); } + return this.client.set( key, JSON.stringify(value, this.getCircularReplacer()), ); } + + if (!skipPrepare) { + // try to get old key value + const keyValue = await this.get(key, CacheGetType.TYPE_OBJECT); + // prepare new key value + value = await this.prepareValue( + value.toString(), + this.getParents(keyValue), + ); + } + return this.client.set(key, value); } else { log(`RedisMockCacheMgr::set: value is empty for ${key}. Skipping ...`); @@ -245,7 +267,11 @@ export default class RedisMockCacheMgr extends CacheMgr { } // set Get Key log(`RedisMockCacheMgr::setList: setting key ${getKey}`); - await this.set(getKey, JSON.stringify(value, this.getCircularReplacer())); + await this.set( + getKey, + JSON.stringify(value, this.getCircularReplacer()), + true, + ); // push Get Key to List listOfGetKeys.push(getKey); } @@ -354,6 +380,7 @@ export default class RedisMockCacheMgr extends CacheMgr { await this.set( key, JSON.stringify(preparedValue, this.getCircularReplacer()), + true, ); list.push(key); @@ -368,17 +395,36 @@ export default class RedisMockCacheMgr extends CacheMgr { if (value && typeof value === 'object') { value[CacheListProp] = listKeys; } else if (value && typeof value === 'string') { - const keyHelper = value.split(CacheListProp); - if (listKeys.length) { - value = `${keyHelper[0]}${CacheListProp}${listKeys.join(',')}`; + const metaHelper = value.split(CacheMetaSplitter); + if (metaHelper.length > 1) { + const keyVal = metaHelper[0]; + const keyMeta = metaHelper[1]; + try { + const meta = JSON.parse(keyMeta); + meta[CacheListProp] = listKeys; + value = `${keyVal}${CacheMetaSplitter}${JSON.stringify(meta)}`; + } catch (e) { + console.error( + `RedisMockCacheMgr::prepareValue: keyValue meta is not JSON`, + keyMeta, + ); + throw new Error( + `RedisMockCacheMgr::prepareValue: keyValue meta is not JSON`, + ); + } + } else { + const meta = { + [CacheListProp]: listKeys, + }; + value = `${value}${CacheMetaSplitter}${JSON.stringify(meta)}`; } } else if (value) { console.error( - `RedisMockCacheMgr::prepareListKey: keyValue is not object or string`, + `RedisMockCacheMgr::prepareValue: keyValue is not object or string`, value, ); throw new Error( - `RedisMockCacheMgr::prepareListKey: keyValue is not object or string`, + `RedisMockCacheMgr::prepareValue: keyValue is not object or string`, ); } return value; diff --git a/packages/nocodb/src/utils/globals.ts b/packages/nocodb/src/utils/globals.ts index 69c7508f2a..34d8d065eb 100644 --- a/packages/nocodb/src/utils/globals.ts +++ b/packages/nocodb/src/utils/globals.ts @@ -176,6 +176,8 @@ export enum CacheDelDirection { CHILD_TO_PARENT = 'CHILD_TO_PARENT', } +export const CacheMetaSplitter = '__nc_meta__'; + export const CacheListProp = '__nc_list__'; export const CacheTimeProp = '__nc_time__';