From 34ea8dcbc2113f78b83798241f3cd04299d053c0 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Wed, 1 Oct 2025 12:28:56 -0400 Subject: [PATCH] manage user keypairs cache --- .../backend/src/core/UserKeypairService.ts | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/packages/backend/src/core/UserKeypairService.ts b/packages/backend/src/core/UserKeypairService.ts index d8a67d273b..048a832908 100644 --- a/packages/backend/src/core/UserKeypairService.ts +++ b/packages/backend/src/core/UserKeypairService.ts @@ -5,16 +5,19 @@ import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; import * as Redis from 'ioredis'; +import { In } from 'typeorm'; import type { MiUser } from '@/models/User.js'; import type { UserKeypairsRepository } from '@/models/_.js'; -import { MemoryKVCache, RedisKVCache } from '@/misc/cache.js'; import type { MiUserKeypair } from '@/models/UserKeypair.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; +import { CacheManagementService, type ManagedQuantumKVCache } from '@/core/CacheManagementService.js'; +import { InternalEventService } from '@/core/InternalEventService.js'; +import type { InternalEventTypes } from '@/core/GlobalEventService.js'; @Injectable() export class UserKeypairService implements OnApplicationShutdown { - private cache: MemoryKVCache; + public readonly userKeypairCache: ManagedQuantumKVCache; constructor( @Inject(DI.redis) @@ -22,18 +25,33 @@ export class UserKeypairService implements OnApplicationShutdown { @Inject(DI.userKeypairsRepository) private userKeypairsRepository: UserKeypairsRepository, + + private readonly internalEventService: InternalEventService, + + cacheManagementService: CacheManagementService, ) { - this.cache = new MemoryKVCache(1000 * 60 * 60 * 24); // 24h + this.userKeypairCache = cacheManagementService.createQuantumKVCache('userKeypair', { + lifetime: 1000 * 60 * 60, // 1h + fetcher: async userId => await this.userKeypairsRepository.findOneBy({ userId }), + bulkFetcher: async userIds => await this.userKeypairsRepository.findBy({ userId: In(userIds) }).then(ks => ks.map(k => [k.userId, k])), + }); + + this.internalEventService.on('userChangeDeletedState', this.onUserDeleted); } @bindThis public async getUserKeypair(userId: MiUser['id']): Promise { - return await this.cache.fetch(userId, () => this.userKeypairsRepository.findOneByOrFail({ userId })); + return await this.userKeypairCache.fetch(userId); + } + + @bindThis + private async onUserDeleted(body: InternalEventTypes['userChangeDeletedState']): Promise { + await this.userKeypairCache.delete(body.id); } @bindThis public dispose(): void { - this.cache.dispose(); + this.internalEventService.off('userChangeDeletedState', this.onUserDeleted); } @bindThis