Browse Source

refactor: revert cache changes

pull/7477/head
Pranav C 8 months ago
parent
commit
6a043a6661
  1. 170
      packages/nocodb/src/cache/RedisCacheMgr.ts

170
packages/nocodb/src/cache/RedisCacheMgr.ts vendored

@ -1,7 +1,11 @@
import debug from 'debug';
import Redis from 'ioredis';
import CacheMgr from './CacheMgr';
import { CacheDelDirection, CacheGetType, CacheScope } from '~/utils/globals';
import {
CacheDelDirection,
CacheGetType,
CacheListProp,
} from '~/utils/globals';
const log = debug('nc:cache');
@ -42,9 +46,15 @@ export default class RedisCacheMgr extends CacheMgr {
};
// @ts-ignore
async del(key: string): Promise<any> {
async del(key: string[] | string): Promise<any> {
log(`RedisCacheMgr::del: deleting key ${key}`);
return this.client.del(key);
if (Array.isArray(key)) {
if (key.length) {
return this.client.del(key);
}
} else if (key) {
return this.client.del(key);
}
}
// @ts-ignore
@ -85,6 +95,10 @@ export default class RedisCacheMgr extends CacheMgr {
if (Array.isArray(value) && value.length) {
return this.client.sadd(key, value);
}
const keyValue = await this.get(key, CacheGetType.TYPE_OBJECT);
if (keyValue) {
value = await this.prepareValue(value, this.getParents(keyValue));
}
return this.client.set(
key,
JSON.stringify(value, this.getCircularReplacer()),
@ -126,30 +140,6 @@ export default class RedisCacheMgr extends CacheMgr {
return this.client.incrby(key, value);
}
// @ts-ignore
async getAll(pattern: string): Promise<any> {
return this.client.hgetall(pattern);
}
// @ts-ignore
async delAll(scope: string, pattern: string): Promise<any[]> {
// Example: nc:<orgs>:model:*:<id>
const keys = await this.client.keys(`${this.prefix}:${scope}:${pattern}`);
log(
`RedisCacheMgr::delAll: deleting all keys with pattern ${this.prefix}:${scope}:${pattern}`,
);
await Promise.all(
keys.map(async (k) => {
await this.deepDel(scope, k, CacheDelDirection.CHILD_TO_PARENT);
}),
);
return Promise.all(
keys.map(async (k) => {
await this.del(k);
}),
);
}
async getList(
scope: string,
subKeys: string[],
@ -169,17 +159,28 @@ export default class RedisCacheMgr extends CacheMgr {
log(`RedisCacheMgr::getList: getting list with key ${key}`);
const isNoneList = arr.length && arr.includes('NONE');
if (isNoneList) {
if (isNoneList || !arr.length) {
return Promise.resolve({
list: [],
isNoneList,
});
}
log(`RedisCacheMgr::getList: getting list with keys ${arr}`);
const values = await this.client.mget(arr);
return {
list: await Promise.all(
arr.map(async (k) => await this.get(k, CacheGetType.TYPE_OBJECT)),
),
list: values.map((res) => {
try {
const o = JSON.parse(res);
if (typeof o === 'object') {
return o;
}
} catch (e) {
return res;
}
return res;
}),
isNoneList,
};
}
@ -212,17 +213,20 @@ export default class RedisCacheMgr extends CacheMgr {
const propValues = props.map((p) => o[p]);
// e.g. nc:<orgs>:<scope>:<prop_value_1>:<prop_value_2>
getKey = `${this.prefix}:${scope}:${propValues.join(':')}`;
}
log(`RedisCacheMgr::setList: get key ${getKey}`);
// get Get Key
let value = await this.get(getKey, CacheGetType.TYPE_OBJECT);
if (value) {
log(`RedisCacheMgr::setList: preparing key ${getKey}`);
// prepare Get Key
value = await this.prepareValue(o, this.getParents(value), listKey);
} else {
// e.g. nc:<orgs>:<scope>:<model_id_1>
getKey = `${this.prefix}:${scope}:${o.id}`;
// special case - MODEL_ROLE_VISIBILITY
if (scope === CacheScope.MODEL_ROLE_VISIBILITY) {
getKey = `${this.prefix}:${scope}:${o.fk_view_id}:${o.role}`;
}
value = await this.prepareValue(o, [], listKey);
}
// set Get Key
log(`RedisCacheMgr::setList: setting key ${getKey}`);
await this.set(getKey, JSON.stringify(o, this.getCircularReplacer()));
await this.set(getKey, JSON.stringify(value, this.getCircularReplacer()));
// push Get Key to List
listOfGetKeys.push(getKey);
}
@ -236,11 +240,11 @@ export default class RedisCacheMgr extends CacheMgr {
key: string,
direction: string,
): Promise<boolean> {
key = `${this.prefix}:${key}`;
log(`RedisCacheMgr::deepDel: choose direction ${direction}`);
if (direction === CacheDelDirection.CHILD_TO_PARENT) {
const childKey = await this.get(key, CacheGetType.TYPE_OBJECT);
// given a child key, delete all keys in corresponding parent lists
const scopeList = await this.client.keys(`${this.prefix}:${scope}*list`);
const scopeList = this.getParents(childKey);
for (const listKey of scopeList) {
// get target list
let list = (await this.get(listKey, CacheGetType.TYPE_ARRAY)) || [];
@ -255,17 +259,17 @@ export default class RedisCacheMgr extends CacheMgr {
if (list.length) {
// set target list
log(`RedisCacheMgr::deepDel: set key ${listKey}`);
await this.del(listKey);
await this.set(listKey, list);
}
}
log(`RedisCacheMgr::deepDel: remove key ${key}`);
return await this.del(key);
} else if (direction === CacheDelDirection.PARENT_TO_CHILD) {
key = /:list$/.test(key) ? key : `${key}:list`;
// given a list key, delete all the children
const listOfChildren = await this.get(key, CacheGetType.TYPE_ARRAY);
// delete each child key
await Promise.all(listOfChildren.map(async (k) => await this.del(k)));
await this.del(listOfChildren);
// delete list key
return await this.del(key);
} else {
@ -297,10 +301,92 @@ export default class RedisCacheMgr extends CacheMgr {
list = [];
await this.del(listKey);
}
log(`RedisCacheMgr::appendToList: get key ${key}`);
// get Get Key
const value = await this.get(key, CacheGetType.TYPE_OBJECT);
log(`RedisCacheMgr::appendToList: preparing key ${key}`);
if (!value) {
// FALLBACK: this is to get rid of all keys that would be effected by this (should never happen)
console.error(`RedisCacheMgr::appendToList: value is empty for ${key}`);
const allParents = [];
// get all children
const listValues = await this.getList(scope, subListKeys);
// get all parents from children
listValues.list.forEach((v) => {
allParents.push(...this.getParents(v));
});
// remove duplicates
const uniqueParents = [...new Set(allParents)];
// delete all parents and children
await Promise.all(
uniqueParents.map(async (p) => {
await this.deepDel(scope, p, CacheDelDirection.PARENT_TO_CHILD);
}),
);
return false;
}
// prepare Get Key
const preparedValue = await this.prepareValue(
value,
this.getParents(value),
listKey,
);
// set Get Key
log(`RedisCacheMgr::appendToList: setting key ${key}`);
await this.set(
key,
JSON.stringify(preparedValue, this.getCircularReplacer()),
);
list.push(key);
return this.set(listKey, list);
}
prepareValue(value, listKeys = [], newParent?) {
if (newParent) {
listKeys.push(newParent);
}
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(',')}`;
}
} else if (value) {
console.error(
`RedisCacheMgr::prepareListKey: keyValue is not object or string`,
value,
);
throw new Error(
`RedisCacheMgr::prepareListKey: keyValue is not object or string`,
);
}
return value;
}
getParents(value) {
if (value && typeof value === 'object') {
if (CacheListProp in value) {
const listsForKey = value[CacheListProp];
if (listsForKey && listsForKey.length) {
return listsForKey;
}
}
} else if (value && typeof value === 'string') {
if (value.includes(CacheListProp)) {
const keyHelper = value.split(CacheListProp);
const listsForKey = keyHelper[1].split(',');
if (listsForKey.length) {
return listsForKey;
}
}
}
return [];
}
async destroy(): Promise<boolean> {
log('RedisCacheMgr::destroy: destroy redis');
return this.client.flushdb().then((r) => r === 'OK');

Loading…
Cancel
Save