From 1377ea4178ffb49cc76466df56ef945fc4c2f713 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 7 Apr 2023 18:48:45 +0900 Subject: [PATCH] perf(backend): improve cache of federated instances --- .../src/core/FederatedInstanceService.ts | 26 +++++++++++++------ .../src/core/entities/UserEntityService.ts | 14 +++------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/packages/backend/src/core/FederatedInstanceService.ts b/packages/backend/src/core/FederatedInstanceService.ts index 2c6d3ac50..bc66591bc 100644 --- a/packages/backend/src/core/FederatedInstanceService.ts +++ b/packages/backend/src/core/FederatedInstanceService.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; +import Redis from 'ioredis'; import type { InstancesRepository } from '@/models/index.js'; import type { Instance } from '@/models/entities/Instance.js'; -import { MemoryKVCache } from '@/misc/cache.js'; +import { MemoryKVCache, RedisKVCache } from '@/misc/cache.js'; import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; import { UtilityService } from '@/core/UtilityService.js'; @@ -9,23 +10,32 @@ import { bindThis } from '@/decorators.js'; @Injectable() export class FederatedInstanceService { - private cache: MemoryKVCache; + public federatedInstanceCache: RedisKVCache; constructor( + @Inject(DI.redis) + private redisClient: Redis.Redis, + @Inject(DI.instancesRepository) private instancesRepository: InstancesRepository, private utilityService: UtilityService, private idService: IdService, ) { - this.cache = new MemoryKVCache(1000 * 60 * 60); + this.federatedInstanceCache = new RedisKVCache(this.redisClient, 'federatedInstance', { + lifetime: 1000 * 60 * 60 * 24, // 24h + memoryCacheLifetime: 1000 * 60 * 30, // 30m + fetcher: (key) => this.instancesRepository.findOneBy({ host: key }), + toRedisConverter: (value) => JSON.stringify(value), + fromRedisConverter: (value) => JSON.parse(value), // TODO: date型の考慮 + }); } @bindThis public async fetch(host: string): Promise { host = this.utilityService.toPuny(host); - const cached = this.cache.get(host); + const cached = await this.federatedInstanceCache.get(host); if (cached) return cached; const index = await this.instancesRepository.findOneBy({ host }); @@ -37,10 +47,10 @@ export class FederatedInstanceService { firstRetrievedAt: new Date(), }).then(x => this.instancesRepository.findOneByOrFail(x.identifiers[0])); - this.cache.set(host, i); + this.federatedInstanceCache.set(host, i); return i; } else { - this.cache.set(host, index); + this.federatedInstanceCache.set(host, index); return index; } } @@ -49,10 +59,10 @@ export class FederatedInstanceService { public async updateCachePartial(host: string, data: Partial): Promise { host = this.utilityService.toPuny(host); - const cached = this.cache.get(host); + const cached = await this.federatedInstanceCache.get(host); if (cached == null) return; - this.cache.set(host, { + this.federatedInstanceCache.set(host, { ...cached, ...data, }); diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index d964b1b1b..cbe94451c 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -9,13 +9,13 @@ import type { Packed } from '@/misc/json-schema.js'; import type { Promiseable } from '@/misc/prelude/await-all.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const.js'; -import { MemoryKVCache, RedisKVCache } from '@/misc/cache.js'; import type { Instance } from '@/models/entities/Instance.js'; import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js'; import { birthdaySchema, descriptionSchema, localUsernameSchema, locationSchema, nameSchema, passwordSchema } from '@/models/entities/User.js'; import type { UsersRepository, UserSecurityKeysRepository, FollowingsRepository, FollowRequestsRepository, BlockingsRepository, MutingsRepository, DriveFilesRepository, NoteUnreadsRepository, ChannelFollowingsRepository, UserNotePiningsRepository, UserProfilesRepository, InstancesRepository, AnnouncementReadsRepository, AnnouncementsRepository, PagesRepository, UserProfile, RenoteMutingsRepository } from '@/models/index.js'; import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; +import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; import type { OnModuleInit } from '@nestjs/common'; import type { AntennaService } from '../AntennaService.js'; import type { CustomEmojiService } from '../CustomEmojiService.js'; @@ -53,7 +53,7 @@ export class UserEntityService implements OnModuleInit { private customEmojiService: CustomEmojiService; private antennaService: AntennaService; private roleService: RoleService; - private userInstanceCache: RedisKVCache; + private federatedInstanceService: FederatedInstanceService; constructor( private moduleRef: ModuleRef, @@ -119,13 +119,6 @@ export class UserEntityService implements OnModuleInit { //private antennaService: AntennaService, //private roleService: RoleService, ) { - this.userInstanceCache = new RedisKVCache(this.redisClient, 'userInstance', { - lifetime: 1000 * 60 * 60 * 24, // 24h - memoryCacheLifetime: 1000 * 60 * 30, // 30m - fetcher: (key) => this.instancesRepository.findOneBy({ host: key }), - toRedisConverter: (value) => JSON.stringify(value), - fromRedisConverter: (value) => JSON.parse(value), // TODO: date型の考慮 - }); } onModuleInit() { @@ -135,6 +128,7 @@ export class UserEntityService implements OnModuleInit { this.customEmojiService = this.moduleRef.get('CustomEmojiService'); this.antennaService = this.moduleRef.get('AntennaService'); this.roleService = this.moduleRef.get('RoleService'); + this.federatedInstanceService = this.moduleRef.get('FederatedInstanceService'); } //#region Validators @@ -349,7 +343,7 @@ export class UserEntityService implements OnModuleInit { avatarBlurhash: user.avatarBlurhash, isBot: user.isBot ?? falsy, isCat: user.isCat ?? falsy, - instance: user.host ? this.userInstanceCache.fetch(user.host).then(instance => instance ? { + instance: user.host ? this.federatedInstanceService.federatedInstanceCache.fetch(user.host).then(instance => instance ? { name: instance.name, softwareName: instance.softwareName, softwareVersion: instance.softwareVersion,