/* * 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'; // This is the one place that's *supposed* to new() up caches. /* eslint-disable no-restricted-syntax */ 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(); } }