diff options
Diffstat (limited to 'packages/backend/src/misc/cache.ts')
| -rw-r--r-- | packages/backend/src/misc/cache.ts | 34 |
1 files changed, 25 insertions, 9 deletions
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<T> { // TODO: メモリ節約のためあまり参照されないキーを定期的に削除できるようにする? -export class MemoryKVCache<T> { - public cache: Map<string, { date: number; value: T; }>; +function nothingToDo<T, V = T>(value: T): V { + return value as unknown as V; +} + +export class MemoryKVCache<T, V = T> { + public cache: Map<string, { date: number; value: V; }>; private lifetime: number; private gcIntervalHandle: NodeJS.Timer; + private toMapConverter: (value: T) => V; + private fromMapConverter: (cached: V) => T | undefined; - constructor(lifetime: MemoryKVCache<never>['lifetime']) { + constructor(lifetime: MemoryKVCache<never>['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<T> { 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<T> { this.cache.delete(key); return undefined; } - return cached.value; + return this.fromMapConverter(cached.value); } @bindThis @@ -222,9 +236,10 @@ export class MemoryKVCache<T> { /** * キャッシュがあればそれを返し、無ければfetcherを呼び出して結果をキャッシュ&返します * optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします + * fetcherの引数はcacheに保存されている値があれば渡されます */ @bindThis - public async fetch(key: string, fetcher: () => Promise<T>, validator?: (cachedValue: T) => boolean): Promise<T> { + public async fetch(key: string, fetcher: (value: V | undefined) => Promise<T>, validator?: (cachedValue: T) => boolean): Promise<T> { const cachedValue = this.get(key); if (cachedValue !== undefined) { if (validator) { @@ -239,7 +254,7 @@ export class MemoryKVCache<T> { } // 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<T> { /** * キャッシュがあればそれを返し、無ければfetcherを呼び出して結果をキャッシュ&返します * optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします + * fetcherの引数はcacheに保存されている値があれば渡されます */ @bindThis - public async fetchMaybe(key: string, fetcher: () => Promise<T | undefined>, validator?: (cachedValue: T) => boolean): Promise<T | undefined> { + public async fetchMaybe(key: string, fetcher: (value: V | undefined) => Promise<T | undefined>, validator?: (cachedValue: T) => boolean): Promise<T | undefined> { const cachedValue = this.get(key); if (cachedValue !== undefined) { if (validator) { @@ -264,7 +280,7 @@ export class MemoryKVCache<T> { } // Cache MISS - const value = await fetcher(); + const value = await fetcher(this.cache.get(key)?.value); if (value !== undefined) { this.set(key, value); } |