From 56c4c4447ba707a00598b97d06b2f87404cd8f4e Mon Sep 17 00:00:00 2001 From: Menci Date: Sun, 21 Apr 2019 13:46:01 +0800 Subject: [PATCH] Add cache for queries with ID --- models/article.ts | 2 ++ models/common.ts | 65 ++++++++++++++++++++++++++++++++++++++++++- models/contest.ts | 2 ++ models/problem.ts | 2 ++ models/problem_tag.ts | 2 ++ models/user.ts | 2 ++ package.json | 2 ++ yarn.lock | 17 +++++++++++ 8 files changed, 93 insertions(+), 1 deletion(-) diff --git a/models/article.ts b/models/article.ts index 6445559..676b92d 100644 --- a/models/article.ts +++ b/models/article.ts @@ -9,6 +9,8 @@ declare var syzoj: any; @TypeORM.Entity() export default class Article extends Model { + static cache = true; + @TypeORM.PrimaryGeneratedColumn() id: number; diff --git a/models/common.ts b/models/common.ts index 5a15346..76a0037 100644 --- a/models/common.ts +++ b/models/common.ts @@ -1,4 +1,6 @@ import * as TypeORM from "typeorm"; +import * as LRUCache from "lru-cache"; +import * as ObjectAssignDeep from "object-assign-deep"; interface Paginater { pageCnt: number; @@ -6,13 +8,74 @@ interface Paginater { currPage: number; } +const caches: Map> = new Map(); + +function ensureCache(modelName) { + if (!caches.has(modelName)) { + caches.set(modelName, new LRUCache({ + max: 500 + })); + } + + return caches.get(modelName); +} + +function cacheGet(modelName, id) { + return ensureCache(modelName).get(parseInt(id)); +} + +function cacheSet(modelName, id, data) { + if (data == null) { + ensureCache(modelName).del(id); + } else { + ensureCache(modelName).set(parseInt(id), data); + } +} + export default class Model extends TypeORM.BaseEntity { + static cache = false; + static async findById(this: TypeORM.ObjectType, id?: number): Promise { - return await (this as any).findOne(parseInt(id as any) || 0); + const doQuery = async () => await (this as any).findOne(parseInt(id as any) || 0); + + if ((this as typeof Model).cache) { + let result; + if (result = cacheGet(this.name, id)) { + // Got cached result. + // Return a copy of it to avoid issue of adding or assigning properties. + const object = {}; + Object.keys(result).forEach(key => { + object[key] = result[key] && typeof result[key] === 'object' + ? ObjectAssignDeep({}, result[key]) + : result[key]; + }); + return (this as typeof Model).create(object); + } + + result = await doQuery(); + if (result) { + cacheSet(this.name, id, result); + } + return result; + } else { + return await doQuery(); + } } async destroy() { + const id = (this as any).id; await TypeORM.getManager().remove(this); + if ((this.constructor as typeof Model).cache) { + cacheSet(this.constructor.name, id, null); + } + } + + async save(): Promise { + await super.save(); + if ((this.constructor as typeof Model).cache) { + cacheSet(this.constructor.name, (this as any).id, this); + } + return this; } static async countQuery(this: TypeORM.ObjectType, query: TypeORM.SelectQueryBuilder | string) { diff --git a/models/contest.ts b/models/contest.ts index bce9905..1f279da 100644 --- a/models/contest.ts +++ b/models/contest.ts @@ -16,6 +16,8 @@ enum ContestType { @TypeORM.Entity() export default class Contest extends Model { + static cache = true; + @TypeORM.PrimaryGeneratedColumn() id: number; diff --git a/models/problem.ts b/models/problem.ts index 3698a72..0bd8c65 100644 --- a/models/problem.ts +++ b/models/problem.ts @@ -200,6 +200,8 @@ enum ProblemType { @TypeORM.Entity() export default class Problem extends Model { + static cache = true; + @TypeORM.PrimaryGeneratedColumn() id: number; diff --git a/models/problem_tag.ts b/models/problem_tag.ts index ca059b8..f24afe5 100644 --- a/models/problem_tag.ts +++ b/models/problem_tag.ts @@ -3,6 +3,8 @@ import Model from "./common"; @TypeORM.Entity() export default class ProblemTag extends Model { + static cache = true; + @TypeORM.PrimaryGeneratedColumn() id: number; diff --git a/models/user.ts b/models/user.ts index 48a4c58..ab7ef87 100644 --- a/models/user.ts +++ b/models/user.ts @@ -9,6 +9,8 @@ import Article from "./article"; @TypeORM.Entity() export default class User extends Model { + static cache = true; + @TypeORM.PrimaryGeneratedColumn() id: number; diff --git a/package.json b/package.json index a25eb0a..029ebc4 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "homepage": "https://github.com/syzoj/syzoj#readme", "dependencies": { "@types/fs-extra": "^5.0.5", + "@types/lru-cache": "^5.1.0", "ansi-to-html": "^0.6.10", "async-lock": "^1.2.0", "bluebird": "^3.5.4", @@ -45,6 +46,7 @@ "jsdom": "^14.0.0", "jsondiffpatch": "0.2.5", "jsonwebtoken": "^8.5.1", + "lru-cache": "^5.1.1", "mariadb": "^2.0.3", "moment": "^2.24.0", "msgpack-lite": "^0.1.26", diff --git a/yarn.lock b/yarn.lock index b60bf1c..eb31abf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14,6 +14,11 @@ dependencies: "@types/node" "*" +"@types/lru-cache@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.0.tgz#57f228f2b80c046b4a1bd5cac031f81f207f4f03" + integrity sha512-RaE0B+14ToE4l6UqdarKPnXwVDuigfFv+5j9Dze/Nqr23yyuqdNvzcZi3xB+3Agvi5R4EOgAksfv3lXX4vBt9w== + "@types/node@*": version "11.11.4" resolved "https://registry.yarnpkg.com/@types/node/-/node-11.11.4.tgz#8808bd5a82bbf6f5d412eff1c228d178e7c24bb3" @@ -2135,6 +2140,13 @@ lru-cache@^4.0.1, lru-cache@^4.1.3: pseudomap "^1.0.2" yallist "^2.1.2" +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + mailcomposer@^3.12.0: version "3.12.0" resolved "https://registry.yarnpkg.com/mailcomposer/-/mailcomposer-3.12.0.tgz#9c5e1188aa8e1c62ec8b86bd43468102b639e8f9" @@ -3842,6 +3854,11 @@ yallist@^2.1.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= +yallist@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" + integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== + yargonaut@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/yargonaut/-/yargonaut-1.1.4.tgz#c64f56432c7465271221f53f5cc517890c3d6e0c"