From a908252c5705234dfe44242e2aa9f4912d5ea166 Mon Sep 17 00:00:00 2001 From: undefined Date: Sun, 8 Aug 2021 17:35:20 +0800 Subject: [PATCH] core: multi-worker user cache --- packages/hydrooj/package.json | 2 +- packages/hydrooj/src/model/training.ts | 8 ++++---- packages/hydrooj/src/model/user.ts | 23 +++++++++++++---------- packages/hydrooj/src/service/bus.ts | 1 + 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/packages/hydrooj/package.json b/packages/hydrooj/package.json index 0d3005295..a2f4decff 100644 --- a/packages/hydrooj/package.json +++ b/packages/hydrooj/package.json @@ -1,6 +1,6 @@ { "name": "hydrooj", - "version": "2.32.6", + "version": "2.32.7", "bin": "bin/hydrooj.js", "main": "src/loader", "module": "src/loader", diff --git a/packages/hydrooj/src/model/training.ts b/packages/hydrooj/src/model/training.ts index 25da09448..ec852238e 100644 --- a/packages/hydrooj/src/model/training.ts +++ b/packages/hydrooj/src/model/training.ts @@ -104,8 +104,11 @@ export async function get(domainId: string, tid: ObjectID) { return tdoc; } +export const getMulti = (domainId: string, query: FilterQuery = {}) => + document.getMulti(domainId, document.TYPE_TRAINING, query).sort({ pin: -1, _id: -1 }); + export async function getList(domainId: string, tids: ObjectID[]) { - const tdocs = await this.getMulti( + const tdocs = await getMulti( domainId, { _id: { $in: Array.from(new Set(tids)) } }, ).toArray(); const r = {}; @@ -113,9 +116,6 @@ export async function getList(domainId: string, tids: ObjectID[]) { return r; } -export const getMulti = (domainId: string, query: FilterQuery = {}) => - document.getMulti(domainId, document.TYPE_TRAINING, query).sort({ pin: -1, _id: -1 }); - global.Hydro.model.training = { getPids, isDone, diff --git a/packages/hydrooj/src/model/user.ts b/packages/hydrooj/src/model/user.ts index bfd1e5fc2..521618e2c 100644 --- a/packages/hydrooj/src/model/user.ts +++ b/packages/hydrooj/src/model/user.ts @@ -21,18 +21,21 @@ const coll: Collection = db.collection('user'); const logger = new Logger('model/user'); const cache = new LRU({ max: 500, maxAge: 300 * 1000 }); -export function deleteUserCache(udoc: User | Udoc | string) { +export function deleteUserCache(udoc: User | Udoc | string | undefined | null) { + if (!udoc) return; + bus.broadcast('user/delcache', JSON.stringify(udoc)); if (typeof udoc === 'string') { for (const key of cache.keys().filter((k) => k.endsWith(`/${udoc}`))) { cache.del(key); } - } else if (udoc) { + } else { const id = [udoc._id.toString(), udoc.uname.toLowerCase(), udoc.mail.toLowerCase()]; for (const key of cache.keys().filter((k) => id.includes(k.split('/')[0]))) { cache.del(key); } } } +bus.on('user/delcache', (content) => deleteUserCache(JSON.parse(content))); class User implements _User { _id: number; @@ -145,7 +148,7 @@ class UserModel { @ArgMethod static async getById(domainId: string, _id: number, scope: bigint | string = PERM.PERM_ALL): Promise { - if (cache.has(`${_id}/${domainId}`)) return cache.get(`${_id}/${domainId}`); + if (cache.has(`${_id}/${domainId}`)) return cache.get(`${_id}/${domainId}`) || null; const udoc = await coll.findOne({ _id }); if (!udoc) return null; const dudoc = await domain.getDomainUser(domainId, udoc); @@ -159,16 +162,16 @@ class UserModel { static async getList(domainId: string, uids: number[]): Promise { const _uids = new Set(uids); - const r = {}; + const r: Udict = {}; // eslint-disable-next-line no-await-in-loop - for (const uid of _uids) r[uid] = (await UserModel.getById(domainId, uid)) || UserModel.defaultUser; + for (const uid of _uids) r[uid] = (await UserModel.getById(domainId, uid)) || new User(UserModel.defaultUser, {}); return r; } @ArgMethod static async getByUname(domainId: string, uname: string): Promise { const unameLower = uname.trim().toLowerCase(); - if (cache.has(`${unameLower}/${domainId}`)) return cache.get(`${unameLower}/${domainId}`); + if (cache.has(`${unameLower}/${domainId}`)) return cache.get(`${unameLower}/${domainId}`) || null; const udoc = await coll.findOne({ unameLower }); if (!udoc) return null; const dudoc = await domain.getDomainUser(domainId, udoc); @@ -182,7 +185,7 @@ class UserModel { @ArgMethod static async getByEmail(domainId: string, mail: string): Promise { const mailLower = mail.trim().toLowerCase(); - if (cache.has(`${mailLower}/${domainId}`)) return cache.get(`${mailLower}/${domainId}`); + if (cache.has(`${mailLower}/${domainId}`)) return cache.get(`${mailLower}/${domainId}`) || null; const udoc = await coll.findOne({ mailLower }); if (!udoc) return null; const dudoc = await domain.getDomainUser(domainId, udoc); @@ -209,7 +212,7 @@ class UserModel { } @ArgMethod - static async setPassword(uid: number, password: string): Promise { + static async setPassword(uid: number, password: string): Promise { const salt = String.random(); const res = await coll.findOneAndUpdate( { _id: uid }, @@ -217,7 +220,7 @@ class UserModel { { returnDocument: 'after' }, ); deleteUserCache(res.value); - return res.value; + return res.value || null; } @ArgMethod @@ -300,7 +303,7 @@ class UserModel { { returnDocument: 'after' }, ); deleteUserCache(res.value); - return res.value; + return res.value || null; } @ArgMethod diff --git a/packages/hydrooj/src/service/bus.ts b/packages/hydrooj/src/service/bus.ts index 4615611f4..b56b567a6 100644 --- a/packages/hydrooj/src/service/bus.ts +++ b/packages/hydrooj/src/service/bus.ts @@ -52,6 +52,7 @@ export interface EventMap extends Record { 'user/message': (uid: number, mdoc: MessageDoc) => void 'user/get': (udoc: User) => void + 'user/delcache': (content: string) => void 'domain/create': (ddoc: DomainDoc) => VoidReturn 'domain/before-get': (query: FilterQuery) => VoidReturn