Browse Source

fix: user cache

pull/7202/head
mertmit 12 months ago
parent
commit
e8b4678803
  1. 10
      packages/nocodb/src/helpers/initAdminFromEnv.ts
  2. 12
      packages/nocodb/src/models/Base.ts
  3. 201
      packages/nocodb/src/models/BaseUser.ts
  4. 5
      packages/nocodb/src/models/User.ts
  5. 17
      packages/nocodb/src/services/base-users/base-users.service.ts
  6. 4
      packages/nocodb/src/utils/globals.ts

10
packages/nocodb/src/helpers/initAdminFromEnv.ts

@ -169,11 +169,6 @@ export default async function initAdminFromEnv(_ncMeta = Noco.ncMeta) {
existingUserWithNewEmail.id, existingUserWithNewEmail.id,
); );
// clear cache
await NocoCache.delAll(
CacheScope.USER,
`${existingUserWithNewEmail.email}___*`,
);
await NocoCache.del( await NocoCache.del(
`${CacheScope.USER}:${existingUserWithNewEmail.id}`, `${CacheScope.USER}:${existingUserWithNewEmail.id}`,
); );
@ -237,11 +232,6 @@ export default async function initAdminFromEnv(_ncMeta = Noco.ncMeta) {
// check user account already present with the new admin email // check user account already present with the new admin email
const existingUserWithNewEmail = await User.getByEmail(email, ncMeta); const existingUserWithNewEmail = await User.getByEmail(email, ncMeta);
if (existingUserWithNewEmail?.id) { if (existingUserWithNewEmail?.id) {
// clear cache
await NocoCache.delAll(
CacheScope.USER,
`${existingUserWithNewEmail.email}___*`,
);
await NocoCache.del( await NocoCache.del(
`${CacheScope.USER}:${existingUserWithNewEmail.id}`, `${CacheScope.USER}:${existingUserWithNewEmail.id}`,
); );

12
packages/nocodb/src/models/Base.ts

@ -210,8 +210,6 @@ export default class Base implements BaseType {
await NocoCache.del(`${CacheScope.PROJECT}:ref:${o.id}`); await NocoCache.del(`${CacheScope.PROJECT}:ref:${o.id}`);
} }
await NocoCache.delAll(CacheScope.USER_PROJECT, '*');
await NocoCache.del(CacheScope.INSTANCE_META); await NocoCache.del(CacheScope.INSTANCE_META);
// remove item in cache list // remove item in cache list
@ -321,8 +319,6 @@ export default class Base implements BaseType {
await NocoCache.del(`${CacheScope.PROJECT}:ref:${base.id}`); await NocoCache.del(`${CacheScope.PROJECT}:ref:${base.id}`);
} }
await NocoCache.delAll(CacheScope.USER_PROJECT, '*');
await NocoCache.deepDel( await NocoCache.deepDel(
CacheScope.PROJECT, CacheScope.PROJECT,
`${CacheScope.PROJECT}:${baseId}`, `${CacheScope.PROJECT}:${baseId}`,
@ -360,7 +356,9 @@ export default class Base implements BaseType {
static async getWithInfoByTitle(title: string, ncMeta = Noco.ncMeta) { static async getWithInfoByTitle(title: string, ncMeta = Noco.ncMeta) {
const base = await this.getByTitle(title, ncMeta); const base = await this.getByTitle(title, ncMeta);
if (base) await base.getSources(ncMeta); if (base) {
await base.getSources(ncMeta);
}
return base; return base;
} }
@ -442,7 +440,9 @@ export default class Base implements BaseType {
// parse meta // parse meta
base.meta = parseMetaProp(base); base.meta = parseMetaProp(base);
if (base) await base.getSources(ncMeta); if (base) {
await base.getSources(ncMeta);
}
return base; return base;
} }

201
packages/nocodb/src/models/BaseUser.ts

@ -1,8 +1,9 @@
import { ProjectRoles } from 'nocodb-sdk'; import { ProjectRoles } from 'nocodb-sdk';
import type { BaseType } from 'nocodb-sdk'; import type { BaseType } from 'nocodb-sdk';
import User from '~/models/User'; import type User from '~/models/User';
import Base from '~/models/Base'; import Base from '~/models/Base';
import { import {
CacheDelDirection,
// CacheDelDirection, // CacheDelDirection,
CacheGetType, CacheGetType,
CacheScope, CacheScope,
@ -44,10 +45,15 @@ export default class BaseUser {
true, true,
); );
// reset all user bases cache const res = this.get(base_id, fk_user_id, ncMeta);
await NocoCache.delAll(CacheScope.USER_PROJECT, `${baseUser.fk_user_id}:*`);
return this.get(base_id, fk_user_id, ncMeta); await NocoCache.appendToList(
CacheScope.BASE_USER,
[base_id],
`${CacheScope.BASE_USER}:${base_id}:${fk_user_id}`,
);
return res;
} }
// public static async update(id, user: Partial<BaseUser>, ncMeta = Noco.ncMeta) { // public static async update(id, user: Partial<BaseUser>, ncMeta = Noco.ncMeta) {
@ -58,7 +64,7 @@ export default class BaseUser {
baseId && baseId &&
userId && userId &&
(await NocoCache.get( (await NocoCache.get(
`${CacheScope.PROJECT_USER}:${baseId}:${userId}`, `${CacheScope.BASE_USER}:${baseId}:${userId}`,
CacheGetType.TYPE_OBJECT, CacheGetType.TYPE_OBJECT,
)); ));
if (!baseUser) { if (!baseUser) {
@ -67,7 +73,7 @@ export default class BaseUser {
base_id: baseId, base_id: baseId,
}); });
await NocoCache.set( await NocoCache.set(
`${CacheScope.PROJECT_USER}:${baseId}:${userId}`, `${CacheScope.BASE_USER}:${baseId}:${userId}`,
baseUser, baseUser,
); );
} }
@ -88,39 +94,50 @@ export default class BaseUser {
}, },
ncMeta = Noco.ncMeta, ncMeta = Noco.ncMeta,
): Promise<(Partial<User> & BaseUser)[]> { ): Promise<(Partial<User> & BaseUser)[]> {
const queryBuilder = ncMeta const cachedList = await NocoCache.getList(CacheScope.BASE_USER, [base_id]);
.knex(MetaTable.USERS) let { list: baseUsers } = cachedList;
.select( const { isNoneList } = cachedList;
`${MetaTable.USERS}.id`, if (!isNoneList && !baseUsers.length) {
`${MetaTable.USERS}.email`, const queryBuilder = ncMeta
`${MetaTable.USERS}.invite_token`, .knex(MetaTable.USERS)
`${MetaTable.USERS}.roles as main_roles`, .select(
`${MetaTable.USERS}.created_at as created_at`, `${MetaTable.USERS}.id`,
`${MetaTable.PROJECT_USERS}.base_id`, `${MetaTable.USERS}.email`,
`${MetaTable.PROJECT_USERS}.roles as roles`, `${MetaTable.USERS}.invite_token`,
); `${MetaTable.USERS}.roles as main_roles`,
`${MetaTable.USERS}.created_at as created_at`,
`${MetaTable.PROJECT_USERS}.base_id`,
`${MetaTable.PROJECT_USERS}.roles as roles`,
);
if (limit) { if (limit) {
queryBuilder.offset(offset).limit(limit); queryBuilder.offset(offset).limit(limit);
} }
if (query) { if (query) {
queryBuilder.where('email', 'like', `%${query.toLowerCase?.()}%`); queryBuilder.where('email', 'like', `%${query.toLowerCase?.()}%`);
} }
queryBuilder.leftJoin(MetaTable.PROJECT_USERS, function () { queryBuilder.leftJoin(MetaTable.PROJECT_USERS, function () {
this.on( this.on(
`${MetaTable.PROJECT_USERS}.fk_user_id`, `${MetaTable.PROJECT_USERS}.fk_user_id`,
'=', '=',
`${MetaTable.USERS}.id`, `${MetaTable.USERS}.id`,
).andOn( ).andOn(
`${MetaTable.PROJECT_USERS}.base_id`, `${MetaTable.PROJECT_USERS}.base_id`,
'=', '=',
ncMeta.knex.raw('?', [base_id]), ncMeta.knex.raw('?', [base_id]),
); );
}); });
const baseUsers = await queryBuilder; baseUsers = await queryBuilder;
baseUsers = baseUsers.map((baseUser) => {
return this.castType(baseUser);
});
await NocoCache.setList(CacheScope.BASE_USER, [base_id], baseUsers);
}
return baseUsers; return baseUsers;
} }
@ -163,26 +180,14 @@ export default class BaseUser {
ncMeta = Noco.ncMeta, ncMeta = Noco.ncMeta,
) { ) {
// get existing cache // get existing cache
const key = `${CacheScope.PROJECT_USER}:${baseId}:${userId}`; const key = `${CacheScope.BASE_USER}:${baseId}:${userId}`;
const o = await NocoCache.get(key, CacheGetType.TYPE_OBJECT); const o = await NocoCache.get(key, CacheGetType.TYPE_OBJECT);
if (o) { if (o) {
o.roles = roles; o.roles = roles;
// set cache // set cache
await NocoCache.set(key, o); await NocoCache.set(key, o);
} }
// update user cache
const user = await User.get(userId);
if (user) {
const email = user.email;
for (const key of [`${CacheScope.USER}:${email}___${baseId}`]) {
const o = await NocoCache.get(key, CacheGetType.TYPE_OBJECT);
if (o) {
o.roles = roles;
// set cache
await NocoCache.set(key, o);
}
}
}
// set meta // set meta
return await ncMeta.metaUpdate( return await ncMeta.metaUpdate(
null, null,
@ -206,76 +211,41 @@ export default class BaseUser {
) { ) {
const updateObj = extractProps(baseUser, ['starred', 'hidden', 'order']); const updateObj = extractProps(baseUser, ['starred', 'hidden', 'order']);
// get existing cache const key = `${CacheScope.BASE_USER}:${baseId}:${userId}`;
const key = `${CacheScope.PROJECT_USER}:${baseId}:${userId}`;
const o = await NocoCache.get(key, CacheGetType.TYPE_OBJECT);
if (o) {
Object.assign(o, updateObj);
// set cache
await NocoCache.set(key, o);
}
// update user cache
const user = await User.get(userId);
if (user) {
const email = user.email;
for (const key of [
`${CacheScope.USER}:${email}`,
`${CacheScope.USER}:${email}___${baseId}`,
]) {
const o = await NocoCache.get(key, CacheGetType.TYPE_OBJECT);
if (o) {
Object.assign(o, updateObj);
// set cache
await NocoCache.set(key, o);
}
}
}
// set meta // set meta
return await ncMeta.metaUpdate( await ncMeta.metaUpdate(null, null, MetaTable.PROJECT_USERS, updateObj, {
fk_user_id: userId,
base_id: baseId,
});
// delete cache
await NocoCache.del(key);
// cache and return
return await this.get(baseId, userId, ncMeta);
}
static async delete(baseId: string, userId: string, ncMeta = Noco.ncMeta) {
// delete meta
const response = await ncMeta.metaDelete(
null, null,
null, null,
MetaTable.PROJECT_USERS, MetaTable.PROJECT_USERS,
updateObj,
{ {
fk_user_id: userId, fk_user_id: userId,
base_id: baseId, base_id: baseId,
}, },
); );
}
static async delete(baseId: string, userId: string, ncMeta = Noco.ncMeta) {
const { email } = await ncMeta.metaGet2(null, null, MetaTable.USERS, {
id: userId,
});
if (email) {
await NocoCache.delAll(CacheScope.USER, `${email}*`);
}
// remove base from user base list cache // delete cache
const cachedList = await NocoCache.getList(CacheScope.USER_PROJECT, [ await NocoCache.deepDel(
userId, CacheScope.BASE_USER,
]); `${CacheScope.BASE_USER}:${baseId}:${userId}`,
let { list: cachedProjectList } = cachedList; CacheDelDirection.CHILD_TO_PARENT,
const { isNoneList } = cachedList; );
if (!isNoneList && cachedProjectList?.length) {
cachedProjectList = cachedProjectList.filter((p) => p.id !== baseId);
// delete the whole list first so that the old one won't be included
await NocoCache.del(`${CacheScope.USER_PROJECT}:${userId}:list`);
if (cachedProjectList.length > 0) {
// set the updated list (i.e. excluding the to-be-deleted base id)
await NocoCache.setList(
CacheScope.USER_PROJECT,
[userId],
cachedProjectList,
);
}
}
await NocoCache.del(`${CacheScope.PROJECT_USER}:${baseId}:${userId}`); return response;
return await ncMeta.metaDelete(null, null, MetaTable.PROJECT_USERS, {
fk_user_id: userId,
base_id: baseId,
});
} }
static async getProjectsIdList( static async getProjectsIdList(
@ -292,18 +262,7 @@ export default class BaseUser {
params: any, params: any,
ncMeta = Noco.ncMeta, ncMeta = Noco.ncMeta,
): Promise<BaseType[]> { ): Promise<BaseType[]> {
// let baseList: BaseType[]; // TODO implement CacheScope.USER_BASE
// todo: pagination
// todo: caching based on filter type
// = await NocoCache.getList(CacheScope.USER_PROJECT, [
// userId,
// ]);
// if (baseList.length) {
// return baseList;
// }
const qb = ncMeta const qb = ncMeta
.knex(MetaTable.PROJECT) .knex(MetaTable.PROJECT)
.select(`${MetaTable.PROJECT}.id`) .select(`${MetaTable.PROJECT}.id`)
@ -375,8 +334,6 @@ export default class BaseUser {
for (const base of baseList) { for (const base of baseList) {
base.meta = parseMetaProp(base); base.meta = parseMetaProp(base);
} }
await NocoCache.setList(CacheScope.USER_PROJECT, [userId], baseList);
} }
const castedProjectList = baseList const castedProjectList = baseList

5
packages/nocodb/src/models/User.ts

@ -106,9 +106,6 @@ export default class User implements UserType {
// delete the email-based cache to avoid unexpected behaviour since we can update email as well // delete the email-based cache to avoid unexpected behaviour since we can update email as well
await NocoCache.del(`${CacheScope.USER}:${existingUser.email}`); await NocoCache.del(`${CacheScope.USER}:${existingUser.email}`);
// as <baseId> is unknown, delete user:<email>___<baseId> in cache
await NocoCache.delAll(CacheScope.USER, `${existingUser.email}___*`);
// get existing cache // get existing cache
const keys = [ const keys = [
// update user:<id> // update user:<id>
@ -240,8 +237,6 @@ export default class User implements UserType {
if (!user) NcError.badRequest('User not found'); if (!user) NcError.badRequest('User not found');
// clear all user related cache // clear all user related cache
await NocoCache.delAll(CacheScope.USER, `${userId}___*`);
await NocoCache.delAll(CacheScope.USER, `${user.email}___*`);
await NocoCache.del(`${CacheScope.USER}:${userId}`); await NocoCache.del(`${CacheScope.USER}:${userId}`);
await NocoCache.del(`${CacheScope.USER}:${user.email}`); await NocoCache.del(`${CacheScope.USER}:${user.email}`);

17
packages/nocodb/src/services/base-users/base-users.service.ts

@ -11,7 +11,6 @@ import * as ejs from 'ejs';
import validator from 'validator'; import validator from 'validator';
import type { ProjectUserReqType, UserType } from 'nocodb-sdk'; import type { ProjectUserReqType, UserType } from 'nocodb-sdk';
import type { NcRequest } from '~/interface/config'; import type { NcRequest } from '~/interface/config';
import NocoCache from '~/cache/NocoCache';
import { validatePayload } from '~/helpers'; import { validatePayload } from '~/helpers';
import Noco from '~/Noco'; import Noco from '~/Noco';
import { AppHooksService } from '~/services/app-hooks/app-hooks.service'; import { AppHooksService } from '~/services/app-hooks/app-hooks.service';
@ -20,8 +19,7 @@ import NcPluginMgrv2 from '~/helpers/NcPluginMgrv2';
import { PagedResponseImpl } from '~/helpers/PagedResponse'; import { PagedResponseImpl } from '~/helpers/PagedResponse';
import { randomTokenString } from '~/helpers/stringHelpers'; import { randomTokenString } from '~/helpers/stringHelpers';
import { Base, BaseUser, User } from '~/models'; import { Base, BaseUser, User } from '~/models';
import { MetaTable } from '~/utils/globals';
import { CacheGetType, CacheScope, MetaTable } from '~/utils/globals';
import { extractProps } from '~/helpers/extractProps'; import { extractProps } from '~/helpers/extractProps';
import { getProjectRolePower } from '~/utils/roleHelper'; import { getProjectRolePower } from '~/utils/roleHelper';
@ -131,19 +129,6 @@ export class BaseUsersService {
ip: param.req.clientIp, ip: param.req.clientIp,
req: param.req, req: param.req,
}); });
const cachedUser = await NocoCache.get(
`${CacheScope.USER}:${email}___${param.baseId}`,
CacheGetType.TYPE_OBJECT,
);
if (cachedUser) {
cachedUser.roles = param.baseUser.roles || 'editor';
await NocoCache.set(
`${CacheScope.USER}:${email}___${param.baseId}`,
cachedUser,
);
}
} else { } else {
try { try {
// create new user with invite token // create new user with invite token

4
packages/nocodb/src/utils/globals.ts

@ -149,11 +149,11 @@ export enum CacheScope {
AUDIT = 'audit', AUDIT = 'audit',
HOOK = 'hook', HOOK = 'hook',
PLUGIN = 'plugin', PLUGIN = 'plugin',
PROJECT_USER = 'baseUser', BASE_USER = 'baseUser',
MODEL_ROLE_VISIBILITY = 'modelRoleVisibility', MODEL_ROLE_VISIBILITY = 'modelRoleVisibility',
API_TOKEN = 'apiToken', API_TOKEN = 'apiToken',
INSTANCE_META = 'instanceMeta', INSTANCE_META = 'instanceMeta',
USER_PROJECT = 'userProject', USER_BASE = 'userBase',
DASHBOARD_PROJECT_DB_PROJECT_LINKING = 'dashboardProjectDBProjectLinking', DASHBOARD_PROJECT_DB_PROJECT_LINKING = 'dashboardProjectDBProjectLinking',
SINGLE_QUERY = 'singleQuery', SINGLE_QUERY = 'singleQuery',
JOBS = 'nc_jobs', JOBS = 'nc_jobs',

Loading…
Cancel
Save