From 28647de1968995e294e2ade365a29685df91eae6 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Wed, 5 Apr 2023 12:10:40 +0900
Subject: [PATCH] enhance(backend): improve userkeypair cache

---
 packages/backend/src/core/CoreModule.ts       | 12 +++++------
 .../src/core/UserKeypairStoreService.ts       | 20 ++++++++++++++-----
 .../src/core/activitypub/ApRendererService.ts |  8 ++++----
 .../src/core/activitypub/ApRequestService.ts  |  8 ++++----
 .../src/server/ActivityPubServerService.ts    |  6 +++---
 5 files changed, 32 insertions(+), 22 deletions(-)

diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts
index 5c867e6cfc..ea6e229610 100644
--- a/packages/backend/src/core/CoreModule.ts
+++ b/packages/backend/src/core/CoreModule.ts
@@ -40,7 +40,7 @@ import { TwoFactorAuthenticationService } from './TwoFactorAuthenticationService
 import { UserBlockingService } from './UserBlockingService.js';
 import { CacheService } from './CacheService.js';
 import { UserFollowingService } from './UserFollowingService.js';
-import { UserKeypairStoreService } from './UserKeypairStoreService.js';
+import { UserKeypairService } from './UserKeypairService.js';
 import { UserListService } from './UserListService.js';
 import { UserMutingService } from './UserMutingService.js';
 import { UserSuspendService } from './UserSuspendService.js';
@@ -161,7 +161,7 @@ const $TwoFactorAuthenticationService: Provider = { provide: 'TwoFactorAuthentic
 const $UserBlockingService: Provider = { provide: 'UserBlockingService', useExisting: UserBlockingService };
 const $CacheService: Provider = { provide: 'CacheService', useExisting: CacheService };
 const $UserFollowingService: Provider = { provide: 'UserFollowingService', useExisting: UserFollowingService };
-const $UserKeypairStoreService: Provider = { provide: 'UserKeypairStoreService', useExisting: UserKeypairStoreService };
+const $UserKeypairService: Provider = { provide: 'UserKeypairService', useExisting: UserKeypairService };
 const $UserListService: Provider = { provide: 'UserListService', useExisting: UserListService };
 const $UserMutingService: Provider = { provide: 'UserMutingService', useExisting: UserMutingService };
 const $UserSuspendService: Provider = { provide: 'UserSuspendService', useExisting: UserSuspendService };
@@ -284,7 +284,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		UserBlockingService,
 		CacheService,
 		UserFollowingService,
-		UserKeypairStoreService,
+		UserKeypairService,
 		UserListService,
 		UserMutingService,
 		UserSuspendService,
@@ -401,7 +401,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		$UserBlockingService,
 		$CacheService,
 		$UserFollowingService,
-		$UserKeypairStoreService,
+		$UserKeypairService,
 		$UserListService,
 		$UserMutingService,
 		$UserSuspendService,
@@ -519,7 +519,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		UserBlockingService,
 		CacheService,
 		UserFollowingService,
-		UserKeypairStoreService,
+		UserKeypairService,
 		UserListService,
 		UserMutingService,
 		UserSuspendService,
@@ -635,7 +635,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		$UserBlockingService,
 		$CacheService,
 		$UserFollowingService,
-		$UserKeypairStoreService,
+		$UserKeypairService,
 		$UserListService,
 		$UserMutingService,
 		$UserSuspendService,
diff --git a/packages/backend/src/core/UserKeypairStoreService.ts b/packages/backend/src/core/UserKeypairStoreService.ts
index 872a0335ea..22a9fb2b8e 100644
--- a/packages/backend/src/core/UserKeypairStoreService.ts
+++ b/packages/backend/src/core/UserKeypairStoreService.ts
@@ -1,24 +1,34 @@
 import { Inject, Injectable } from '@nestjs/common';
+import Redis from 'ioredis';
 import type { User } from '@/models/entities/User.js';
 import type { UserKeypairsRepository } from '@/models/index.js';
-import { MemoryKVCache } from '@/misc/cache.js';
+import { RedisKVCache } from '@/misc/cache.js';
 import type { UserKeypair } from '@/models/entities/UserKeypair.js';
 import { DI } from '@/di-symbols.js';
 import { bindThis } from '@/decorators.js';
 
 @Injectable()
-export class UserKeypairStoreService {
-	private cache: MemoryKVCache<UserKeypair>;
+export class UserKeypairService {
+	private cache: RedisKVCache<UserKeypair>;
 
 	constructor(
+		@Inject(DI.redis)
+		private redisClient: Redis.Redis,
+
 		@Inject(DI.userKeypairsRepository)
 		private userKeypairsRepository: UserKeypairsRepository,
 	) {
-		this.cache = new MemoryKVCache<UserKeypair>(Infinity);
+		this.cache = new RedisKVCache<UserKeypair>(this.redisClient, 'userKeypair', {
+			lifetime: 1000 * 60 * 60 * 24, // 24h
+			memoryCacheLifetime: Infinity,
+			fetcher: (key) => this.userKeypairsRepository.findOneByOrFail({ userId: key }),
+			toRedisConverter: (value) => JSON.stringify(value),
+			fromRedisConverter: (value) => JSON.parse(value),
+		});
 	}
 
 	@bindThis
 	public async getUserKeypair(userId: User['id']): Promise<UserKeypair> {
-		return await this.cache.fetch(userId, () => this.userKeypairsRepository.findOneByOrFail({ userId: userId }));
+		return await this.cache.fetch(userId);
 	}
 }
diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts
index 0d03e3d904..15512c8f47 100644
--- a/packages/backend/src/core/activitypub/ApRendererService.ts
+++ b/packages/backend/src/core/activitypub/ApRendererService.ts
@@ -14,7 +14,7 @@ import type { NoteReaction } from '@/models/entities/NoteReaction.js';
 import type { Emoji } from '@/models/entities/Emoji.js';
 import type { Poll } from '@/models/entities/Poll.js';
 import type { PollVote } from '@/models/entities/PollVote.js';
-import { UserKeypairStoreService } from '@/core/UserKeypairStoreService.js';
+import { UserKeypairService } from '@/core/UserKeypairService.js';
 import { MfmService } from '@/core/MfmService.js';
 import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
@@ -53,7 +53,7 @@ export class ApRendererService {
 		private userEntityService: UserEntityService,
 		private driveFileEntityService: DriveFileEntityService,
 		private ldSignatureService: LdSignatureService,
-		private userKeypairStoreService: UserKeypairStoreService,
+		private userKeypairService: UserKeypairService,
 		private apMfmService: ApMfmService,
 		private mfmService: MfmService,
 	) {
@@ -473,7 +473,7 @@ export class ApRendererService {
 			...hashtagTags,
 		];
 
-		const keypair = await this.userKeypairStoreService.getUserKeypair(user.id);
+		const keypair = await this.userKeypairService.getUserKeypair(user.id);
 
 		const person = {
 			type: isSystem ? 'Application' : user.isBot ? 'Service' : 'Person',
@@ -640,7 +640,7 @@ export class ApRendererService {
 
 	@bindThis
 	public async attachLdSignature(activity: any, user: { id: User['id']; host: null; }): Promise<IActivity> {
-		const keypair = await this.userKeypairStoreService.getUserKeypair(user.id);
+		const keypair = await this.userKeypairService.getUserKeypair(user.id);
 
 		const ldSignature = this.ldSignatureService.use();
 		ldSignature.debug = false;
diff --git a/packages/backend/src/core/activitypub/ApRequestService.ts b/packages/backend/src/core/activitypub/ApRequestService.ts
index 71fbc29476..5005612ab8 100644
--- a/packages/backend/src/core/activitypub/ApRequestService.ts
+++ b/packages/backend/src/core/activitypub/ApRequestService.ts
@@ -4,7 +4,7 @@ import { Inject, Injectable } from '@nestjs/common';
 import { DI } from '@/di-symbols.js';
 import type { Config } from '@/config.js';
 import type { User } from '@/models/entities/User.js';
-import { UserKeypairStoreService } from '@/core/UserKeypairStoreService.js';
+import { UserKeypairService } from '@/core/UserKeypairService.js';
 import { HttpRequestService } from '@/core/HttpRequestService.js';
 import { LoggerService } from '@/core/LoggerService.js';
 import { bindThis } from '@/decorators.js';
@@ -131,7 +131,7 @@ export class ApRequestService {
 		@Inject(DI.config)
 		private config: Config,
 
-		private userKeypairStoreService: UserKeypairStoreService,
+		private userKeypairService: UserKeypairService,
 		private httpRequestService: HttpRequestService,
 		private loggerService: LoggerService,
 	) {
@@ -143,7 +143,7 @@ export class ApRequestService {
 	public async signedPost(user: { id: User['id'] }, url: string, object: any) {
 		const body = JSON.stringify(object);
 
-		const keypair = await this.userKeypairStoreService.getUserKeypair(user.id);
+		const keypair = await this.userKeypairService.getUserKeypair(user.id);
 
 		const req = ApRequestCreator.createSignedPost({
 			key: {
@@ -170,7 +170,7 @@ export class ApRequestService {
 	 */
 	@bindThis
 	public async signedGet(url: string, user: { id: User['id'] }) {
-		const keypair = await this.userKeypairStoreService.getUserKeypair(user.id);
+		const keypair = await this.userKeypairService.getUserKeypair(user.id);
 
 		const req = ApRequestCreator.createSignedGet({
 			key: {
diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts
index da8d0114e5..5799622074 100644
--- a/packages/backend/src/server/ActivityPubServerService.ts
+++ b/packages/backend/src/server/ActivityPubServerService.ts
@@ -12,7 +12,7 @@ import type { Config } from '@/config.js';
 import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
 import { QueueService } from '@/core/QueueService.js';
 import type { LocalUser, User } from '@/models/entities/User.js';
-import { UserKeypairStoreService } from '@/core/UserKeypairStoreService.js';
+import { UserKeypairService } from '@/core/UserKeypairService.js';
 import type { Following } from '@/models/entities/Following.js';
 import { countIf } from '@/misc/prelude/array.js';
 import type { Note } from '@/models/entities/Note.js';
@@ -58,7 +58,7 @@ export class ActivityPubServerService {
 		private userEntityService: UserEntityService,
 		private apRendererService: ApRendererService,
 		private queueService: QueueService,
-		private userKeypairStoreService: UserKeypairStoreService,
+		private userKeypairService: UserKeypairService,
 		private queryService: QueryService,
 	) {
 		//this.createServer = this.createServer.bind(this);
@@ -540,7 +540,7 @@ export class ActivityPubServerService {
 				return;
 			}
 
-			const keypair = await this.userKeypairStoreService.getUserKeypair(user.id);
+			const keypair = await this.userKeypairService.getUserKeypair(user.id);
 
 			if (this.userEntityService.isLocalUser(user)) {
 				reply.header('Cache-Control', 'public, max-age=180');