From 239cc3de6d5bf8a46966792549071ec53782259d Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Wed, 1 Oct 2025 11:26:01 -0400 Subject: [PATCH] implement CacheManagementService to provided centralized management of cache instances --- .../src/core/CacheManagementService.ts | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 packages/backend/src/core/CacheManagementService.ts diff --git a/packages/backend/src/core/CacheManagementService.ts b/packages/backend/src/core/CacheManagementService.ts new file mode 100644 index 0000000000..180572e300 --- /dev/null +++ b/packages/backend/src/core/CacheManagementService.ts @@ -0,0 +1,109 @@ +/* + * SPDX-FileCopyrightText: hazelnoot and other Sharkey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; +import * as Redis from 'ioredis'; +import { + MemoryKVCache, + MemorySingleCache, + RedisKVCache, + RedisSingleCache, + type RedisKVCacheOpts, + type RedisSingleCacheOpts, +} from '@/misc/cache.js'; +import { QuantumKVCache, type QuantumKVOpts } from '@/misc/QuantumKVCache.js'; +import { bindThis } from '@/decorators.js'; +import { DI } from '@/di-symbols.js'; +import { TimeService } from '@/core/TimeService.js'; +import { InternalEventService } from '@/core/InternalEventService.js'; + +export type ManagedMemoryKVCache = Managed>; +export type ManagedMemorySingleCache = Managed>; +export type ManagedRedisKVCache = Managed>; +export type ManagedRedisSingleCache = Managed>; +export type ManagedQuantumKVCache = Managed>; + +export type Managed = Omit; +export type Manager = { dispose(): void, clear(): void }; + +/** + * Creates and "manages" instances of any standard cache type. + * Instances produced by this class are automatically tracked for disposal when the application shuts down. + */ +@Injectable() +export class CacheManagementService implements OnApplicationShutdown { + private readonly managedCaches = new Set(); + + constructor( + @Inject(DI.redis) + private readonly redisClient: Redis.Redis, + + private readonly timeService: TimeService, + private readonly internalEventService: InternalEventService, + ) {} + + private get cacheServices() { + return { + internalEventService: this.internalEventService, + redisClient: this.redisClient, + timeService: this.timeService, + }; + } + + @bindThis + public createMemoryKVCache(lifetime: number): ManagedMemoryKVCache { + const cache = new MemoryKVCache(lifetime, this.cacheServices); + return this.manageCache(cache); + } + + @bindThis + public createMemorySingleCache(lifetime: number): ManagedMemorySingleCache { + const cache = new MemorySingleCache(lifetime, this.cacheServices); + return this.manageCache(cache); + } + + @bindThis + public createRedisKVCache(name: string, opts: RedisKVCacheOpts): ManagedRedisKVCache { + const cache = new RedisKVCache(name, this.cacheServices, opts); + return this.manageCache(cache); + } + + @bindThis + public createRedisSingleCache(name: string, opts: RedisSingleCacheOpts): ManagedRedisSingleCache { + const cache = new RedisSingleCache(name, this.cacheServices, opts); + return this.manageCache(cache); + } + + @bindThis + public createQuantumKVCache(name: string, opts: QuantumKVOpts): ManagedQuantumKVCache { + const cache = new QuantumKVCache(name, this.cacheServices, opts); + return this.manageCache(cache); + } + + protected manageCache(cache: T): Managed { + this.managedCaches.add(cache); + return cache; + } + + @bindThis + public clear(): void { + for (const manager of this.managedCaches) { + manager.clear(); + } + } + + @bindThis + public async dispose(): Promise { + for (const manager of this.managedCaches) { + manager.dispose(); + } + this.managedCaches.clear(); + } + + @bindThis + public async onApplicationShutdown(): Promise { + await this.dispose(); + } +}