Skip to content

Commit

Permalink
enhance(backend): improve cache
Browse files Browse the repository at this point in the history
  • Loading branch information
syuilo committed Apr 4, 2023
1 parent 7f3afac commit ecaf152
Show file tree
Hide file tree
Showing 21 changed files with 184 additions and 89 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Inject, Injectable } from '@nestjs/common';
import Redis from 'ioredis';
import type { UsersRepository } from '@/models/index.js';
import { MemoryKVCache } from '@/misc/cache.js';
import type { UserProfile, UsersRepository } from '@/models/index.js';
import { MemoryKVCache, RedisKVCache } from '@/misc/cache.js';
import type { LocalUser, User } from '@/models/entities/User.js';
import { DI } from '@/di-symbols.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
Expand All @@ -10,13 +10,18 @@ import { StreamMessages } from '@/server/api/stream/types.js';
import type { OnApplicationShutdown } from '@nestjs/common';

@Injectable()
export class UserCacheService implements OnApplicationShutdown {
export class CacheService implements OnApplicationShutdown {
public userByIdCache: MemoryKVCache<User>;
public localUserByNativeTokenCache: MemoryKVCache<LocalUser | null>;
public localUserByIdCache: MemoryKVCache<LocalUser>;
public uriPersonCache: MemoryKVCache<User | null>;
public userProfileCache: RedisKVCache<UserProfile>;
public userMutingsCache: RedisKVCache<string[]>;

constructor(
@Inject(DI.redis)
private redisClient: Redis.Redis,

@Inject(DI.redisSubscriber)
private redisSubscriber: Redis.Redis,

Expand All @@ -31,6 +36,8 @@ export class UserCacheService implements OnApplicationShutdown {
this.localUserByNativeTokenCache = new MemoryKVCache<LocalUser | null>(Infinity);
this.localUserByIdCache = new MemoryKVCache<LocalUser>(Infinity);
this.uriPersonCache = new MemoryKVCache<User | null>(Infinity);
this.userProfileCache = new RedisKVCache<UserProfile>(this.redisClient, 'userProfile', 1000 * 60 * 60 * 24, 1000 * 60);
this.userMutingsCache = new RedisKVCache<string[]>(this.redisClient, 'userMutings', 1000 * 60 * 60 * 24, 1000 * 60);

this.redisSubscriber.on('message', this.onMessage);
}
Expand All @@ -52,7 +59,7 @@ export class UserCacheService implements OnApplicationShutdown {
}
}
if (this.userEntityService.isLocalUser(user)) {
this.localUserByNativeTokenCache.set(user.token, user);
this.localUserByNativeTokenCache.set(user.token!, user);
this.localUserByIdCache.set(user.id, user);
}
break;
Expand All @@ -77,7 +84,7 @@ export class UserCacheService implements OnApplicationShutdown {
}

@bindThis
public findById(userId: User['id']) {
public findUserById(userId: User['id']) {
return this.userByIdCache.fetch(userId, () => this.usersRepository.findOneByOrFail({ id: userId }));
}

Expand Down
12 changes: 6 additions & 6 deletions packages/backend/src/core/CoreModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import { S3Service } from './S3Service.js';
import { SignupService } from './SignupService.js';
import { TwoFactorAuthenticationService } from './TwoFactorAuthenticationService.js';
import { UserBlockingService } from './UserBlockingService.js';
import { UserCacheService } from './UserCacheService.js';
import { CacheService } from './CacheService.js';
import { UserFollowingService } from './UserFollowingService.js';
import { UserKeypairStoreService } from './UserKeypairStoreService.js';
import { UserListService } from './UserListService.js';
Expand Down Expand Up @@ -159,7 +159,7 @@ const $S3Service: Provider = { provide: 'S3Service', useExisting: S3Service };
const $SignupService: Provider = { provide: 'SignupService', useExisting: SignupService };
const $TwoFactorAuthenticationService: Provider = { provide: 'TwoFactorAuthenticationService', useExisting: TwoFactorAuthenticationService };
const $UserBlockingService: Provider = { provide: 'UserBlockingService', useExisting: UserBlockingService };
const $UserCacheService: Provider = { provide: 'UserCacheService', useExisting: UserCacheService };
const $CacheService: Provider = { provide: 'CacheService', useExisting: CacheService };
const $UserFollowingService: Provider = { provide: 'UserFollowingService', useExisting: UserFollowingService };
const $UserKeypairStoreService: Provider = { provide: 'UserKeypairStoreService', useExisting: UserKeypairStoreService };
const $UserListService: Provider = { provide: 'UserListService', useExisting: UserListService };
Expand Down Expand Up @@ -282,7 +282,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
SignupService,
TwoFactorAuthenticationService,
UserBlockingService,
UserCacheService,
CacheService,
UserFollowingService,
UserKeypairStoreService,
UserListService,
Expand Down Expand Up @@ -399,7 +399,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$SignupService,
$TwoFactorAuthenticationService,
$UserBlockingService,
$UserCacheService,
$CacheService,
$UserFollowingService,
$UserKeypairStoreService,
$UserListService,
Expand Down Expand Up @@ -517,7 +517,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
SignupService,
TwoFactorAuthenticationService,
UserBlockingService,
UserCacheService,
CacheService,
UserFollowingService,
UserKeypairStoreService,
UserListService,
Expand Down Expand Up @@ -633,7 +633,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$SignupService,
$TwoFactorAuthenticationService,
$UserBlockingService,
$UserCacheService,
$CacheService,
$UserFollowingService,
$UserKeypairStoreService,
$UserListService,
Expand Down
12 changes: 6 additions & 6 deletions packages/backend/src/core/InstanceActorService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
import { IsNull } from 'typeorm';
import type { LocalUser } from '@/models/entities/User.js';
import type { UsersRepository } from '@/models/index.js';
import { MemoryKVCache } from '@/misc/cache.js';
import { MemoryCache } from '@/misc/cache.js';
import { DI } from '@/di-symbols.js';
import { CreateSystemUserService } from '@/core/CreateSystemUserService.js';
import { bindThis } from '@/decorators.js';
Expand All @@ -11,20 +11,20 @@ const ACTOR_USERNAME = 'instance.actor' as const;

@Injectable()
export class InstanceActorService {
private cache: MemoryKVCache<LocalUser>;
private cache: MemoryCache<LocalUser>;

constructor(
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,

private createSystemUserService: CreateSystemUserService,
) {
this.cache = new MemoryKVCache<LocalUser>(Infinity);
this.cache = new MemoryCache<LocalUser>(Infinity);
}

@bindThis
public async getInstanceActor(): Promise<LocalUser> {
const cached = this.cache.get(null);
const cached = this.cache.get();
if (cached) return cached;

const user = await this.usersRepository.findOneBy({
Expand All @@ -33,11 +33,11 @@ export class InstanceActorService {
}) as LocalUser | undefined;

if (user) {
this.cache.set(null, user);
this.cache.set(user);
return user;
} else {
const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME) as LocalUser;
this.cache.set(null, created);
this.cache.set(created);
return created;
}
}
Expand Down
6 changes: 3 additions & 3 deletions packages/backend/src/core/NoteCreateService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js
import { checkWordMute } from '@/misc/check-word-mute.js';
import type { Channel } from '@/models/entities/Channel.js';
import { normalizeForSearch } from '@/misc/normalize-for-search.js';
import { MemoryKVCache } from '@/misc/cache.js';
import { MemoryCache } from '@/misc/cache.js';
import type { UserProfile } from '@/models/entities/UserProfile.js';
import { RelayService } from '@/core/RelayService.js';
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
Expand All @@ -47,7 +47,7 @@ import { DB_MAX_NOTE_TEXT_LENGTH } from '@/const.js';
import { RoleService } from '@/core/RoleService.js';
import { MetaService } from '@/core/MetaService.js';

const mutedWordsCache = new MemoryKVCache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5);
const mutedWordsCache = new MemoryCache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5);

type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';

Expand Down Expand Up @@ -473,7 +473,7 @@ export class NoteCreateService implements OnApplicationShutdown {
this.incNotesCountOfUser(user);

// Word mute
mutedWordsCache.fetch(null, () => this.userProfilesRepository.find({
mutedWordsCache.fetch(() => this.userProfilesRepository.find({
where: {
enableWordMute: true,
},
Expand Down
18 changes: 7 additions & 11 deletions packages/backend/src/core/NotificationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Redis from 'ioredis';
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
import { In } from 'typeorm';
import { DI } from '@/di-symbols.js';
import type { MutingsRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js';
import type { MutingsRepository, UserProfile, UserProfilesRepository, UsersRepository } from '@/models/index.js';
import type { User } from '@/models/entities/User.js';
import type { Notification } from '@/models/entities/Notification.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
Expand All @@ -12,6 +12,7 @@ import { GlobalEventService } from '@/core/GlobalEventService.js';
import { PushNotificationService } from '@/core/PushNotificationService.js';
import { NotificationEntityService } from '@/core/entities/NotificationEntityService.js';
import { IdService } from '@/core/IdService.js';
import { CacheService } from '@/core/CacheService.js';

@Injectable()
export class NotificationService implements OnApplicationShutdown {
Expand All @@ -35,6 +36,7 @@ export class NotificationService implements OnApplicationShutdown {
private idService: IdService,
private globalEventService: GlobalEventService,
private pushNotificationService: PushNotificationService,
private cacheService: CacheService,
) {
}

Expand All @@ -49,7 +51,6 @@ export class NotificationService implements OnApplicationShutdown {
'+',
'-',
'COUNT', 1);
console.log('latestNotificationIdsRes', latestNotificationIdsRes);
const latestNotificationId = latestNotificationIdsRes[0]?.[0];

if (latestNotificationId == null) return;
Expand All @@ -72,22 +73,17 @@ export class NotificationService implements OnApplicationShutdown {
type: Notification['type'],
data: Partial<Notification>,
): Promise<Notification | null> {
// TODO: Cache
const profile = await this.userProfilesRepository.findOneBy({ userId: notifieeId });
const isMuted = profile?.mutingNotificationTypes.includes(type);
const profile = await this.cacheService.userProfileCache.fetch(notifieeId, () => this.userProfilesRepository.findOneByOrFail({ userId: notifieeId }));
const isMuted = profile.mutingNotificationTypes.includes(type);
if (isMuted) return null;

if (data.notifierId) {
if (notifieeId === data.notifierId) {
return null;
}

// TODO: cache
const mutings = await this.mutingsRepository.findOneBy({
muterId: notifieeId,
muteeId: data.notifierId,
});
if (mutings) {
const mutings = await this.cacheService.userMutingsCache.fetch(notifieeId, () => this.mutingsRepository.findBy({ muterId: notifieeId }).then(xs => xs.map(x => x.muteeId)));
if (mutings.includes(data.notifierId)) {
return null;
}
}
Expand Down
8 changes: 4 additions & 4 deletions packages/backend/src/core/RelayService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { IsNull } from 'typeorm';
import type { LocalUser, User } from '@/models/entities/User.js';
import type { RelaysRepository, UsersRepository } from '@/models/index.js';
import { IdService } from '@/core/IdService.js';
import { MemoryKVCache } from '@/misc/cache.js';
import { MemoryCache } from '@/misc/cache.js';
import type { Relay } from '@/models/entities/Relay.js';
import { QueueService } from '@/core/QueueService.js';
import { CreateSystemUserService } from '@/core/CreateSystemUserService.js';
Expand All @@ -16,7 +16,7 @@ const ACTOR_USERNAME = 'relay.actor' as const;

@Injectable()
export class RelayService {
private relaysCache: MemoryKVCache<Relay[]>;
private relaysCache: MemoryCache<Relay[]>;

constructor(
@Inject(DI.usersRepository)
Expand All @@ -30,7 +30,7 @@ export class RelayService {
private createSystemUserService: CreateSystemUserService,
private apRendererService: ApRendererService,
) {
this.relaysCache = new MemoryKVCache<Relay[]>(1000 * 60 * 10);
this.relaysCache = new MemoryCache<Relay[]>(1000 * 60 * 10);
}

@bindThis
Expand Down Expand Up @@ -109,7 +109,7 @@ export class RelayService {
public async deliverToRelays(user: { id: User['id']; host: null; }, activity: any): Promise<void> {
if (activity == null) return;

const relays = await this.relaysCache.fetch(null, () => this.relaysRepository.findBy({
const relays = await this.relaysCache.fetch(() => this.relaysRepository.findBy({
status: 'accepted',
}));
if (relays.length === 0) return;
Expand Down
30 changes: 15 additions & 15 deletions packages/backend/src/core/RoleService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { Inject, Injectable } from '@nestjs/common';
import Redis from 'ioredis';
import { In } from 'typeorm';
import type { Role, RoleAssignment, RoleAssignmentsRepository, RolesRepository, UsersRepository } from '@/models/index.js';
import { MemoryKVCache } from '@/misc/cache.js';
import { MemoryKVCache, MemoryCache } from '@/misc/cache.js';
import type { User } from '@/models/entities/User.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { MetaService } from '@/core/MetaService.js';
import { UserCacheService } from '@/core/UserCacheService.js';
import { CacheService } from '@/core/CacheService.js';
import type { RoleCondFormulaValue } from '@/models/entities/Role.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { StreamMessages } from '@/server/api/stream/types.js';
Expand Down Expand Up @@ -57,7 +57,7 @@ export const DEFAULT_POLICIES: RolePolicies = {

@Injectable()
export class RoleService implements OnApplicationShutdown {
private rolesCache: MemoryKVCache<Role[]>;
private rolesCache: MemoryCache<Role[]>;
private roleAssignmentByUserIdCache: MemoryKVCache<RoleAssignment[]>;

public static AlreadyAssignedError = class extends Error {};
Expand All @@ -77,14 +77,14 @@ export class RoleService implements OnApplicationShutdown {
private roleAssignmentsRepository: RoleAssignmentsRepository,

private metaService: MetaService,
private userCacheService: UserCacheService,
private cacheService: CacheService,
private userEntityService: UserEntityService,
private globalEventService: GlobalEventService,
private idService: IdService,
) {
//this.onMessage = this.onMessage.bind(this);

this.rolesCache = new MemoryKVCache<Role[]>(Infinity);
this.rolesCache = new MemoryCache<Role[]>(Infinity);
this.roleAssignmentByUserIdCache = new MemoryKVCache<RoleAssignment[]>(Infinity);

this.redisSubscriber.on('message', this.onMessage);
Expand All @@ -98,7 +98,7 @@ export class RoleService implements OnApplicationShutdown {
const { type, body } = obj.message as StreamMessages['internal']['payload'];
switch (type) {
case 'roleCreated': {
const cached = this.rolesCache.get(null);
const cached = this.rolesCache.get();
if (cached) {
cached.push({
...body,
Expand All @@ -110,7 +110,7 @@ export class RoleService implements OnApplicationShutdown {
break;
}
case 'roleUpdated': {
const cached = this.rolesCache.get(null);
const cached = this.rolesCache.get();
if (cached) {
const i = cached.findIndex(x => x.id === body.id);
if (i > -1) {
Expand All @@ -125,9 +125,9 @@ export class RoleService implements OnApplicationShutdown {
break;
}
case 'roleDeleted': {
const cached = this.rolesCache.get(null);
const cached = this.rolesCache.get();
if (cached) {
this.rolesCache.set(null, cached.filter(x => x.id !== body.id));
this.rolesCache.set(cached.filter(x => x.id !== body.id));
}
break;
}
Expand Down Expand Up @@ -214,9 +214,9 @@ export class RoleService implements OnApplicationShutdown {
// 期限切れのロールを除外
assigns = assigns.filter(a => a.expiresAt == null || (a.expiresAt.getTime() > now));
const assignedRoleIds = assigns.map(x => x.roleId);
const roles = await this.rolesCache.fetch(null, () => this.rolesRepository.findBy({}));
const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({}));
const assignedRoles = roles.filter(r => assignedRoleIds.includes(r.id));
const user = roles.some(r => r.target === 'conditional') ? await this.userCacheService.findById(userId) : null;
const user = roles.some(r => r.target === 'conditional') ? await this.cacheService.findUserById(userId) : null;
const matchedCondRoles = roles.filter(r => r.target === 'conditional' && this.evalCond(user!, r.condFormula));
return [...assignedRoles, ...matchedCondRoles];
}
Expand All @@ -231,11 +231,11 @@ export class RoleService implements OnApplicationShutdown {
// 期限切れのロールを除外
assigns = assigns.filter(a => a.expiresAt == null || (a.expiresAt.getTime() > now));
const assignedRoleIds = assigns.map(x => x.roleId);
const roles = await this.rolesCache.fetch(null, () => this.rolesRepository.findBy({}));
const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({}));
const assignedBadgeRoles = roles.filter(r => r.asBadge && assignedRoleIds.includes(r.id));
const badgeCondRoles = roles.filter(r => r.asBadge && (r.target === 'conditional'));
if (badgeCondRoles.length > 0) {
const user = roles.some(r => r.target === 'conditional') ? await this.userCacheService.findById(userId) : null;
const user = roles.some(r => r.target === 'conditional') ? await this.cacheService.findUserById(userId) : null;
const matchedBadgeCondRoles = badgeCondRoles.filter(r => this.evalCond(user!, r.condFormula));
return [...assignedBadgeRoles, ...matchedBadgeCondRoles];
} else {
Expand Down Expand Up @@ -301,7 +301,7 @@ export class RoleService implements OnApplicationShutdown {

@bindThis
public async getModeratorIds(includeAdmins = true): Promise<User['id'][]> {
const roles = await this.rolesCache.fetch(null, () => this.rolesRepository.findBy({}));
const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({}));
const moderatorRoles = includeAdmins ? roles.filter(r => r.isModerator || r.isAdministrator) : roles.filter(r => r.isModerator);
const assigns = moderatorRoles.length > 0 ? await this.roleAssignmentsRepository.findBy({
roleId: In(moderatorRoles.map(r => r.id)),
Expand All @@ -321,7 +321,7 @@ export class RoleService implements OnApplicationShutdown {

@bindThis
public async getAdministratorIds(): Promise<User['id'][]> {
const roles = await this.rolesCache.fetch(null, () => this.rolesRepository.findBy({}));
const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({}));
const administratorRoles = roles.filter(r => r.isAdministrator);
const assigns = administratorRoles.length > 0 ? await this.roleAssignmentsRepository.findBy({
roleId: In(administratorRoles.map(r => r.id)),
Expand Down
Loading

0 comments on commit ecaf152

Please sign in to comment.