From 7ec07d5fd2e28d523c85d160944916b2a5ee04a1 Mon Sep 17 00:00:00 2001 From: tamaina Date: Sat, 8 Jul 2023 21:18:16 +0900 Subject: perf(backend): Reduce memory usage of MemoryKVCache (#11076) * perf(backend): Reduce memory usage of MemoryKVCache * fix --- packages/backend/src/misc/cache.ts | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) (limited to 'packages/backend/src/misc/cache.ts') diff --git a/packages/backend/src/misc/cache.ts b/packages/backend/src/misc/cache.ts index f130a7db8b..e825d51371 100644 --- a/packages/backend/src/misc/cache.ts +++ b/packages/backend/src/misc/cache.ts @@ -181,14 +181,28 @@ export class RedisSingleCache { // TODO: メモリ節約のためあまり参照されないキーを定期的に削除できるようにする? -export class MemoryKVCache { - public cache: Map; +function nothingToDo(value: T): V { + return value as unknown as V; +} + +export class MemoryKVCache { + public cache: Map; private lifetime: number; private gcIntervalHandle: NodeJS.Timer; - - constructor(lifetime: MemoryKVCache['lifetime']) { + private toMapConverter: (value: T) => V; + private fromMapConverter: (cached: V) => T | undefined; + + constructor(lifetime: MemoryKVCache['lifetime'], options: { + toMapConverter: (value: T) => V; + fromMapConverter: (cached: V) => T | undefined; + } = { + toMapConverter: nothingToDo, + fromMapConverter: nothingToDo, + }) { this.cache = new Map(); this.lifetime = lifetime; + this.toMapConverter = options.toMapConverter; + this.fromMapConverter = options.fromMapConverter; this.gcIntervalHandle = setInterval(() => { this.gc(); @@ -199,7 +213,7 @@ export class MemoryKVCache { public set(key: string, value: T): void { this.cache.set(key, { date: Date.now(), - value, + value: this.toMapConverter(value), }); } @@ -211,7 +225,7 @@ export class MemoryKVCache { this.cache.delete(key); return undefined; } - return cached.value; + return this.fromMapConverter(cached.value); } @bindThis @@ -222,9 +236,10 @@ export class MemoryKVCache { /** * キャッシュがあればそれを返し、無ければfetcherを呼び出して結果をキャッシュ&返します * optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします + * fetcherの引数はcacheに保存されている値があれば渡されます */ @bindThis - public async fetch(key: string, fetcher: () => Promise, validator?: (cachedValue: T) => boolean): Promise { + public async fetch(key: string, fetcher: (value: V | undefined) => Promise, validator?: (cachedValue: T) => boolean): Promise { const cachedValue = this.get(key); if (cachedValue !== undefined) { if (validator) { @@ -239,7 +254,7 @@ export class MemoryKVCache { } // Cache MISS - const value = await fetcher(); + const value = await fetcher(this.cache.get(key)?.value); this.set(key, value); return value; } @@ -247,9 +262,10 @@ export class MemoryKVCache { /** * キャッシュがあればそれを返し、無ければfetcherを呼び出して結果をキャッシュ&返します * optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします + * fetcherの引数はcacheに保存されている値があれば渡されます */ @bindThis - public async fetchMaybe(key: string, fetcher: () => Promise, validator?: (cachedValue: T) => boolean): Promise { + public async fetchMaybe(key: string, fetcher: (value: V | undefined) => Promise, validator?: (cachedValue: T) => boolean): Promise { const cachedValue = this.get(key); if (cachedValue !== undefined) { if (validator) { @@ -264,7 +280,7 @@ export class MemoryKVCache { } // Cache MISS - const value = await fetcher(); + const value = await fetcher(this.cache.get(key)?.value); if (value !== undefined) { this.set(key, value); } -- cgit v1.2.3-freya