mirror of
				https://codeberg.org/yeentown/barkey.git
				synced 2025-11-03 23:14:13 +00:00 
			
		
		
		
	enhance(backend): improve cache
This commit is contained in:
		
							parent
							
								
									7f3afac0a2
								
							
						
					
					
						commit
						ecaf152b4a
					
				
					 21 changed files with 184 additions and 89 deletions
				
			
		| 
						 | 
				
			
			@ -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';
 | 
			
		||||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			@ -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 }));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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';
 | 
			
		||||
| 
						 | 
				
			
			@ -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 };
 | 
			
		||||
| 
						 | 
				
			
			@ -282,7 +282,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 | 
			
		|||
		SignupService,
 | 
			
		||||
		TwoFactorAuthenticationService,
 | 
			
		||||
		UserBlockingService,
 | 
			
		||||
		UserCacheService,
 | 
			
		||||
		CacheService,
 | 
			
		||||
		UserFollowingService,
 | 
			
		||||
		UserKeypairStoreService,
 | 
			
		||||
		UserListService,
 | 
			
		||||
| 
						 | 
				
			
			@ -399,7 +399,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 | 
			
		|||
		$SignupService,
 | 
			
		||||
		$TwoFactorAuthenticationService,
 | 
			
		||||
		$UserBlockingService,
 | 
			
		||||
		$UserCacheService,
 | 
			
		||||
		$CacheService,
 | 
			
		||||
		$UserFollowingService,
 | 
			
		||||
		$UserKeypairStoreService,
 | 
			
		||||
		$UserListService,
 | 
			
		||||
| 
						 | 
				
			
			@ -517,7 +517,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 | 
			
		|||
		SignupService,
 | 
			
		||||
		TwoFactorAuthenticationService,
 | 
			
		||||
		UserBlockingService,
 | 
			
		||||
		UserCacheService,
 | 
			
		||||
		CacheService,
 | 
			
		||||
		UserFollowingService,
 | 
			
		||||
		UserKeypairStoreService,
 | 
			
		||||
		UserListService,
 | 
			
		||||
| 
						 | 
				
			
			@ -633,7 +633,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 | 
			
		|||
		$SignupService,
 | 
			
		||||
		$TwoFactorAuthenticationService,
 | 
			
		||||
		$UserBlockingService,
 | 
			
		||||
		$UserCacheService,
 | 
			
		||||
		$CacheService,
 | 
			
		||||
		$UserFollowingService,
 | 
			
		||||
		$UserKeypairStoreService,
 | 
			
		||||
		$UserListService,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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';
 | 
			
		||||
| 
						 | 
				
			
			@ -11,7 +11,7 @@ const ACTOR_USERNAME = 'instance.actor' as const;
 | 
			
		|||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class InstanceActorService {
 | 
			
		||||
	private cache: MemoryKVCache<LocalUser>;
 | 
			
		||||
	private cache: MemoryCache<LocalUser>;
 | 
			
		||||
 | 
			
		||||
	constructor(
 | 
			
		||||
		@Inject(DI.usersRepository)
 | 
			
		||||
| 
						 | 
				
			
			@ -19,12 +19,12 @@ export class InstanceActorService {
 | 
			
		|||
 | 
			
		||||
		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({
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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';
 | 
			
		||||
| 
						 | 
				
			
			@ -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';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
			},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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';
 | 
			
		||||
| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
| 
						 | 
				
			
			@ -35,6 +36,7 @@ export class NotificationService implements OnApplicationShutdown {
 | 
			
		|||
		private idService: IdService,
 | 
			
		||||
		private globalEventService: GlobalEventService,
 | 
			
		||||
		private pushNotificationService: PushNotificationService,
 | 
			
		||||
		private cacheService: CacheService,
 | 
			
		||||
	) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -49,7 +51,6 @@ export class NotificationService implements OnApplicationShutdown {
 | 
			
		|||
			'+',
 | 
			
		||||
			'-',
 | 
			
		||||
			'COUNT', 1);
 | 
			
		||||
		console.log('latestNotificationIdsRes', latestNotificationIdsRes);
 | 
			
		||||
		const latestNotificationId = latestNotificationIdsRes[0]?.[0];
 | 
			
		||||
 | 
			
		||||
		if (latestNotificationId == null) return;
 | 
			
		||||
| 
						 | 
				
			
			@ -72,9 +73,8 @@ 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) {
 | 
			
		||||
| 
						 | 
				
			
			@ -82,12 +82,8 @@ export class NotificationService implements OnApplicationShutdown {
 | 
			
		|||
				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;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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';
 | 
			
		||||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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';
 | 
			
		||||
| 
						 | 
				
			
			@ -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 {};
 | 
			
		||||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			@ -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) {
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			@ -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];
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
| 
						 | 
				
			
			@ -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)),
 | 
			
		||||
| 
						 | 
				
			
			@ -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)),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,7 @@ import type { NotesRepository, UserPublickeysRepository, UsersRepository } from
 | 
			
		|||
import type { Config } from '@/config.js';
 | 
			
		||||
import { MemoryKVCache } from '@/misc/cache.js';
 | 
			
		||||
import type { UserPublickey } from '@/models/entities/UserPublickey.js';
 | 
			
		||||
import { UserCacheService } from '@/core/UserCacheService.js';
 | 
			
		||||
import { CacheService } from '@/core/CacheService.js';
 | 
			
		||||
import type { Note } from '@/models/entities/Note.js';
 | 
			
		||||
import { bindThis } from '@/decorators.js';
 | 
			
		||||
import { RemoteUser, User } from '@/models/entities/User.js';
 | 
			
		||||
| 
						 | 
				
			
			@ -47,7 +47,7 @@ export class ApDbResolverService {
 | 
			
		|||
		@Inject(DI.userPublickeysRepository)
 | 
			
		||||
		private userPublickeysRepository: UserPublickeysRepository,
 | 
			
		||||
 | 
			
		||||
		private userCacheService: UserCacheService,
 | 
			
		||||
		private cacheService: CacheService,
 | 
			
		||||
		private apPersonService: ApPersonService,
 | 
			
		||||
	) {
 | 
			
		||||
		this.publicKeyCache = new MemoryKVCache<UserPublickey | null>(Infinity);
 | 
			
		||||
| 
						 | 
				
			
			@ -107,11 +107,11 @@ export class ApDbResolverService {
 | 
			
		|||
		if (parsed.local) {
 | 
			
		||||
			if (parsed.type !== 'users') return null;
 | 
			
		||||
 | 
			
		||||
			return await this.userCacheService.userByIdCache.fetchMaybe(parsed.id, () => this.usersRepository.findOneBy({
 | 
			
		||||
			return await this.cacheService.userByIdCache.fetchMaybe(parsed.id, () => this.usersRepository.findOneBy({
 | 
			
		||||
				id: parsed.id,
 | 
			
		||||
			}).then(x => x ?? undefined)) ?? null;
 | 
			
		||||
		} else {
 | 
			
		||||
			return await this.userCacheService.uriPersonCache.fetch(parsed.uri, () => this.usersRepository.findOneBy({
 | 
			
		||||
			return await this.cacheService.uriPersonCache.fetch(parsed.uri, () => this.usersRepository.findOneBy({
 | 
			
		||||
				uri: parsed.uri,
 | 
			
		||||
			}));
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -138,7 +138,7 @@ export class ApDbResolverService {
 | 
			
		|||
		if (key == null) return null;
 | 
			
		||||
 | 
			
		||||
		return {
 | 
			
		||||
			user: await this.userCacheService.findById(key.userId) as RemoteUser,
 | 
			
		||||
			user: await this.cacheService.findUserById(key.userId) as RemoteUser,
 | 
			
		||||
			key,
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,7 +8,7 @@ import type { Config } from '@/config.js';
 | 
			
		|||
import type { RemoteUser } from '@/models/entities/User.js';
 | 
			
		||||
import { User } from '@/models/entities/User.js';
 | 
			
		||||
import { truncate } from '@/misc/truncate.js';
 | 
			
		||||
import type { UserCacheService } from '@/core/UserCacheService.js';
 | 
			
		||||
import type { CacheService } from '@/core/CacheService.js';
 | 
			
		||||
import { normalizeForSearch } from '@/misc/normalize-for-search.js';
 | 
			
		||||
import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js';
 | 
			
		||||
import type Logger from '@/logger.js';
 | 
			
		||||
| 
						 | 
				
			
			@ -54,7 +54,7 @@ export class ApPersonService implements OnModuleInit {
 | 
			
		|||
	private metaService: MetaService;
 | 
			
		||||
	private federatedInstanceService: FederatedInstanceService;
 | 
			
		||||
	private fetchInstanceMetadataService: FetchInstanceMetadataService;
 | 
			
		||||
	private userCacheService: UserCacheService;
 | 
			
		||||
	private cacheService: CacheService;
 | 
			
		||||
	private apResolverService: ApResolverService;
 | 
			
		||||
	private apNoteService: ApNoteService;
 | 
			
		||||
	private apImageService: ApImageService;
 | 
			
		||||
| 
						 | 
				
			
			@ -97,7 +97,7 @@ export class ApPersonService implements OnModuleInit {
 | 
			
		|||
		//private metaService: MetaService,
 | 
			
		||||
		//private federatedInstanceService: FederatedInstanceService,
 | 
			
		||||
		//private fetchInstanceMetadataService: FetchInstanceMetadataService,
 | 
			
		||||
		//private userCacheService: UserCacheService,
 | 
			
		||||
		//private cacheService: CacheService,
 | 
			
		||||
		//private apResolverService: ApResolverService,
 | 
			
		||||
		//private apNoteService: ApNoteService,
 | 
			
		||||
		//private apImageService: ApImageService,
 | 
			
		||||
| 
						 | 
				
			
			@ -118,7 +118,7 @@ export class ApPersonService implements OnModuleInit {
 | 
			
		|||
		this.metaService = this.moduleRef.get('MetaService');
 | 
			
		||||
		this.federatedInstanceService = this.moduleRef.get('FederatedInstanceService');
 | 
			
		||||
		this.fetchInstanceMetadataService = this.moduleRef.get('FetchInstanceMetadataService');
 | 
			
		||||
		this.userCacheService = this.moduleRef.get('UserCacheService');
 | 
			
		||||
		this.cacheService = this.moduleRef.get('CacheService');
 | 
			
		||||
		this.apResolverService = this.moduleRef.get('ApResolverService');
 | 
			
		||||
		this.apNoteService = this.moduleRef.get('ApNoteService');
 | 
			
		||||
		this.apImageService = this.moduleRef.get('ApImageService');
 | 
			
		||||
| 
						 | 
				
			
			@ -207,14 +207,14 @@ export class ApPersonService implements OnModuleInit {
 | 
			
		|||
	public async fetchPerson(uri: string, resolver?: Resolver): Promise<User | null> {
 | 
			
		||||
		if (typeof uri !== 'string') throw new Error('uri is not string');
 | 
			
		||||
 | 
			
		||||
		const cached = this.userCacheService.uriPersonCache.get(uri);
 | 
			
		||||
		const cached = this.cacheService.uriPersonCache.get(uri);
 | 
			
		||||
		if (cached) return cached;
 | 
			
		||||
 | 
			
		||||
		// URIがこのサーバーを指しているならデータベースからフェッチ
 | 
			
		||||
		if (uri.startsWith(this.config.url + '/')) {
 | 
			
		||||
			const id = uri.split('/').pop();
 | 
			
		||||
			const u = await this.usersRepository.findOneBy({ id });
 | 
			
		||||
			if (u) this.userCacheService.uriPersonCache.set(uri, u);
 | 
			
		||||
			if (u) this.cacheService.uriPersonCache.set(uri, u);
 | 
			
		||||
			return u;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -222,7 +222,7 @@ export class ApPersonService implements OnModuleInit {
 | 
			
		|||
		const exist = await this.usersRepository.findOneBy({ uri });
 | 
			
		||||
 | 
			
		||||
		if (exist) {
 | 
			
		||||
			this.userCacheService.uriPersonCache.set(uri, exist);
 | 
			
		||||
			this.cacheService.uriPersonCache.set(uri, exist);
 | 
			
		||||
			return exist;
 | 
			
		||||
		}
 | 
			
		||||
		//#endregion
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,6 +54,7 @@ export class NotificationEntityService implements OnModuleInit {
 | 
			
		|||
	public async pack(
 | 
			
		||||
		src: Notification,
 | 
			
		||||
		meId: User['id'],
 | 
			
		||||
		// eslint-disable-next-line @typescript-eslint/ban-types
 | 
			
		||||
		options: {
 | 
			
		||||
			
 | 
			
		||||
		},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -255,7 +255,6 @@ export class UserEntityService implements OnModuleInit {
 | 
			
		|||
			'+',
 | 
			
		||||
			'-',
 | 
			
		||||
			'COUNT', 1);
 | 
			
		||||
		console.log('latestNotificationIdsRes', latestNotificationIdsRes);
 | 
			
		||||
		const latestNotificationId = latestNotificationIdsRes[0]?.[0];
 | 
			
		||||
 | 
			
		||||
		return latestNotificationId != null && (latestReadNotificationId == null || latestReadNotificationId < latestNotificationId);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,94 @@
 | 
			
		|||
import Redis from 'ioredis';
 | 
			
		||||
import { bindThis } from '@/decorators.js';
 | 
			
		||||
 | 
			
		||||
// redis通すとDateのインスタンスはstringに変換されるので
 | 
			
		||||
type Serialized<T> = {
 | 
			
		||||
	[K in keyof T]:
 | 
			
		||||
		T[K] extends Date
 | 
			
		||||
			? string
 | 
			
		||||
			: T[K] extends (Date | null)
 | 
			
		||||
				? (string | null)
 | 
			
		||||
				: T[K] extends Record<string, any>
 | 
			
		||||
					? Serialized<T[K]>
 | 
			
		||||
					: T[K];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export class RedisKVCache<T> {
 | 
			
		||||
	private redisClient: Redis.Redis;
 | 
			
		||||
	private name: string;
 | 
			
		||||
	private lifetime: number;
 | 
			
		||||
	private memoryCache: MemoryKVCache<T>;
 | 
			
		||||
 | 
			
		||||
	constructor(redisClient: RedisKVCache<never>['redisClient'], name: RedisKVCache<never>['name'], lifetime: RedisKVCache<never>['lifetime'], memoryCacheLifetime: number) {
 | 
			
		||||
		this.redisClient = redisClient;
 | 
			
		||||
		this.name = name;
 | 
			
		||||
		this.lifetime = lifetime;
 | 
			
		||||
		this.memoryCache = new MemoryKVCache(memoryCacheLifetime);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@bindThis
 | 
			
		||||
	public async set(key: string, value: T): Promise<void> {
 | 
			
		||||
		this.memoryCache.set(key, value);
 | 
			
		||||
		if (this.lifetime === Infinity) {
 | 
			
		||||
			await this.redisClient.set(
 | 
			
		||||
				`kvcache:${this.name}:${key}`,
 | 
			
		||||
				JSON.stringify(value),
 | 
			
		||||
			);
 | 
			
		||||
		} else {
 | 
			
		||||
			await this.redisClient.set(
 | 
			
		||||
				`kvcache:${this.name}:${key}`,
 | 
			
		||||
				JSON.stringify(value),
 | 
			
		||||
				'ex', Math.round(this.lifetime / 1000),
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@bindThis
 | 
			
		||||
	public async get(key: string): Promise<Serialized<T> | T | undefined> {
 | 
			
		||||
		const memoryCached = this.memoryCache.get(key);
 | 
			
		||||
		if (memoryCached !== undefined) return memoryCached;
 | 
			
		||||
 | 
			
		||||
		const cached = await this.redisClient.get(`kvcache:${this.name}:${key}`);
 | 
			
		||||
		if (cached == null) return undefined;
 | 
			
		||||
		return JSON.parse(cached);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@bindThis
 | 
			
		||||
	public async delete(key: string): Promise<void> {
 | 
			
		||||
		this.memoryCache.delete(key);
 | 
			
		||||
		await this.redisClient.del(`kvcache:${this.name}:${key}`);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
 * キャッシュがあればそれを返し、無ければfetcherを呼び出して結果をキャッシュ&返します
 | 
			
		||||
 * optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします
 | 
			
		||||
 */
 | 
			
		||||
	@bindThis
 | 
			
		||||
	public async fetch(key: string, fetcher: () => Promise<T>, validator?: (cachedValue: Serialized<T> | T) => boolean): Promise<Serialized<T> | T> {
 | 
			
		||||
		const cachedValue = await this.get(key);
 | 
			
		||||
		if (cachedValue !== undefined) {
 | 
			
		||||
			if (validator) {
 | 
			
		||||
				if (validator(cachedValue)) {
 | 
			
		||||
					// Cache HIT
 | 
			
		||||
					return cachedValue;
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				// Cache HIT
 | 
			
		||||
				return cachedValue;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Cache MISS
 | 
			
		||||
		const value = await fetcher();
 | 
			
		||||
		this.set(key, value);
 | 
			
		||||
		return value;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: メモリ節約のためあまり参照されないキーを定期的に削除できるようにする?
 | 
			
		||||
 | 
			
		||||
export class MemoryKVCache<T> {
 | 
			
		||||
	public cache: Map<string | null, { date: number; value: T; }>;
 | 
			
		||||
	public cache: Map<string, { date: number; value: T; }>;
 | 
			
		||||
	private lifetime: number;
 | 
			
		||||
 | 
			
		||||
	constructor(lifetime: MemoryKVCache<never>['lifetime']) {
 | 
			
		||||
| 
						 | 
				
			
			@ -12,7 +97,7 @@ export class MemoryKVCache<T> {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	@bindThis
 | 
			
		||||
	public set(key: string | null, value: T): void {
 | 
			
		||||
	public set(key: string, value: T): void {
 | 
			
		||||
		this.cache.set(key, {
 | 
			
		||||
			date: Date.now(),
 | 
			
		||||
			value,
 | 
			
		||||
| 
						 | 
				
			
			@ -20,7 +105,7 @@ export class MemoryKVCache<T> {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	@bindThis
 | 
			
		||||
	public get(key: string | null): T | undefined {
 | 
			
		||||
	public get(key: string): T | undefined {
 | 
			
		||||
		const cached = this.cache.get(key);
 | 
			
		||||
		if (cached == null) return undefined;
 | 
			
		||||
		if ((Date.now() - cached.date) > this.lifetime) {
 | 
			
		||||
| 
						 | 
				
			
			@ -31,7 +116,7 @@ export class MemoryKVCache<T> {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	@bindThis
 | 
			
		||||
	public delete(key: string | null) {
 | 
			
		||||
	public delete(key: string) {
 | 
			
		||||
		this.cache.delete(key);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -40,7 +125,7 @@ export class MemoryKVCache<T> {
 | 
			
		|||
	 * optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします
 | 
			
		||||
	 */
 | 
			
		||||
	@bindThis
 | 
			
		||||
	public async fetch(key: string | null, fetcher: () => Promise<T>, validator?: (cachedValue: T) => boolean): Promise<T> {
 | 
			
		||||
	public async fetch(key: string, fetcher: () => Promise<T>, validator?: (cachedValue: T) => boolean): Promise<T> {
 | 
			
		||||
		const cachedValue = this.get(key);
 | 
			
		||||
		if (cachedValue !== undefined) {
 | 
			
		||||
			if (validator) {
 | 
			
		||||
| 
						 | 
				
			
			@ -65,7 +150,7 @@ export class MemoryKVCache<T> {
 | 
			
		|||
	 * optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします
 | 
			
		||||
	 */
 | 
			
		||||
	@bindThis
 | 
			
		||||
	public async fetchMaybe(key: string | null, fetcher: () => Promise<T | undefined>, validator?: (cachedValue: T) => boolean): Promise<T | undefined> {
 | 
			
		||||
	public async fetchMaybe(key: string, fetcher: () => Promise<T | undefined>, validator?: (cachedValue: T) => boolean): Promise<T | undefined> {
 | 
			
		||||
		const cachedValue = this.get(key);
 | 
			
		||||
		if (cachedValue !== undefined) {
 | 
			
		||||
			if (validator) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,7 +7,7 @@ import { MetaService } from '@/core/MetaService.js';
 | 
			
		|||
import { ApRequestService } from '@/core/activitypub/ApRequestService.js';
 | 
			
		||||
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
 | 
			
		||||
import { FetchInstanceMetadataService } from '@/core/FetchInstanceMetadataService.js';
 | 
			
		||||
import { MemoryKVCache } from '@/misc/cache.js';
 | 
			
		||||
import { MemoryCache } from '@/misc/cache.js';
 | 
			
		||||
import type { Instance } from '@/models/entities/Instance.js';
 | 
			
		||||
import InstanceChart from '@/core/chart/charts/instance.js';
 | 
			
		||||
import ApRequestChart from '@/core/chart/charts/ap-request.js';
 | 
			
		||||
| 
						 | 
				
			
			@ -22,7 +22,7 @@ import type { DeliverJobData } from '../types.js';
 | 
			
		|||
@Injectable()
 | 
			
		||||
export class DeliverProcessorService {
 | 
			
		||||
	private logger: Logger;
 | 
			
		||||
	private suspendedHostsCache: MemoryKVCache<Instance[]>;
 | 
			
		||||
	private suspendedHostsCache: MemoryCache<Instance[]>;
 | 
			
		||||
	private latest: string | null;
 | 
			
		||||
 | 
			
		||||
	constructor(
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +46,7 @@ export class DeliverProcessorService {
 | 
			
		|||
		private queueLoggerService: QueueLoggerService,
 | 
			
		||||
	) {
 | 
			
		||||
		this.logger = this.queueLoggerService.logger.createSubLogger('deliver');
 | 
			
		||||
		this.suspendedHostsCache = new MemoryKVCache<Instance[]>(1000 * 60 * 60);
 | 
			
		||||
		this.suspendedHostsCache = new MemoryCache<Instance[]>(1000 * 60 * 60);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@bindThis
 | 
			
		||||
| 
						 | 
				
			
			@ -60,14 +60,14 @@ export class DeliverProcessorService {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		// isSuspendedなら中断
 | 
			
		||||
		let suspendedHosts = this.suspendedHostsCache.get(null);
 | 
			
		||||
		let suspendedHosts = this.suspendedHostsCache.get();
 | 
			
		||||
		if (suspendedHosts == null) {
 | 
			
		||||
			suspendedHosts = await this.instancesRepository.find({
 | 
			
		||||
				where: {
 | 
			
		||||
					isSuspended: true,
 | 
			
		||||
				},
 | 
			
		||||
			});
 | 
			
		||||
			this.suspendedHostsCache.set(null, suspendedHosts);
 | 
			
		||||
			this.suspendedHostsCache.set(suspendedHosts);
 | 
			
		||||
		}
 | 
			
		||||
		if (suspendedHosts.map(x => x.host).includes(this.utilityService.toPuny(host))) {
 | 
			
		||||
			return 'skip (suspended)';
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ import type { NotesRepository, UsersRepository } from '@/models/index.js';
 | 
			
		|||
import type { Config } from '@/config.js';
 | 
			
		||||
import { MetaService } from '@/core/MetaService.js';
 | 
			
		||||
import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
 | 
			
		||||
import { MemoryKVCache } from '@/misc/cache.js';
 | 
			
		||||
import { MemoryCache } from '@/misc/cache.js';
 | 
			
		||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
 | 
			
		||||
import { bindThis } from '@/decorators.js';
 | 
			
		||||
import NotesChart from '@/core/chart/charts/notes.js';
 | 
			
		||||
| 
						 | 
				
			
			@ -118,17 +118,17 @@ export class NodeinfoServerService {
 | 
			
		|||
			};
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		const cache = new MemoryKVCache<Awaited<ReturnType<typeof nodeinfo2>>>(1000 * 60 * 10);
 | 
			
		||||
		const cache = new MemoryCache<Awaited<ReturnType<typeof nodeinfo2>>>(1000 * 60 * 10);
 | 
			
		||||
 | 
			
		||||
		fastify.get(nodeinfo2_1path, async (request, reply) => {
 | 
			
		||||
			const base = await cache.fetch(null, () => nodeinfo2());
 | 
			
		||||
			const base = await cache.fetch(() => nodeinfo2());
 | 
			
		||||
 | 
			
		||||
			reply.header('Cache-Control', 'public, max-age=600');
 | 
			
		||||
			return { version: '2.1', ...base };
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		fastify.get(nodeinfo2_0path, async (request, reply) => {
 | 
			
		||||
			const base = await cache.fetch(null, () => nodeinfo2());
 | 
			
		||||
			const base = await cache.fetch(() => nodeinfo2());
 | 
			
		||||
 | 
			
		||||
			delete (base as any).software.repository;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,7 @@ import type { LocalUser } from '@/models/entities/User.js';
 | 
			
		|||
import type { AccessToken } from '@/models/entities/AccessToken.js';
 | 
			
		||||
import { MemoryKVCache } from '@/misc/cache.js';
 | 
			
		||||
import type { App } from '@/models/entities/App.js';
 | 
			
		||||
import { UserCacheService } from '@/core/UserCacheService.js';
 | 
			
		||||
import { CacheService } from '@/core/CacheService.js';
 | 
			
		||||
import isNativeToken from '@/misc/is-native-token.js';
 | 
			
		||||
import { bindThis } from '@/decorators.js';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -30,7 +30,7 @@ export class AuthenticateService {
 | 
			
		|||
		@Inject(DI.appsRepository)
 | 
			
		||||
		private appsRepository: AppsRepository,
 | 
			
		||||
 | 
			
		||||
		private userCacheService: UserCacheService,
 | 
			
		||||
		private cacheService: CacheService,
 | 
			
		||||
	) {
 | 
			
		||||
		this.appCache = new MemoryKVCache<App>(Infinity);
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -42,7 +42,7 @@ export class AuthenticateService {
 | 
			
		|||
		}
 | 
			
		||||
	
 | 
			
		||||
		if (isNativeToken(token)) {
 | 
			
		||||
			const user = await this.userCacheService.localUserByNativeTokenCache.fetch(token,
 | 
			
		||||
			const user = await this.cacheService.localUserByNativeTokenCache.fetch(token,
 | 
			
		||||
				() => this.usersRepository.findOneBy({ token }) as Promise<LocalUser | null>);
 | 
			
		||||
	
 | 
			
		||||
			if (user == null) {
 | 
			
		||||
| 
						 | 
				
			
			@ -67,7 +67,7 @@ export class AuthenticateService {
 | 
			
		|||
				lastUsedAt: new Date(),
 | 
			
		||||
			});
 | 
			
		||||
	
 | 
			
		||||
			const user = await this.userCacheService.localUserByIdCache.fetch(accessToken.userId,
 | 
			
		||||
			const user = await this.cacheService.localUserByIdCache.fetch(accessToken.userId,
 | 
			
		||||
				() => this.usersRepository.findOneBy({
 | 
			
		||||
					id: accessToken.userId,
 | 
			
		||||
				}) as Promise<LocalUser>);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,7 +34,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 | 
			
		|||
	) {
 | 
			
		||||
		super(meta, paramDef, async (ps, me) => {
 | 
			
		||||
			const freshUser = await this.usersRepository.findOneByOrFail({ id: me.id });
 | 
			
		||||
			const oldToken = freshUser.token;
 | 
			
		||||
			const oldToken = freshUser.token!;
 | 
			
		||||
 | 
			
		||||
			const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id });
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,6 +18,7 @@ import { AccountUpdateService } from '@/core/AccountUpdateService.js';
 | 
			
		|||
import { HashtagService } from '@/core/HashtagService.js';
 | 
			
		||||
import { DI } from '@/di-symbols.js';
 | 
			
		||||
import { RoleService } from '@/core/RoleService.js';
 | 
			
		||||
import { CacheService } from '@/core/CacheService.js';
 | 
			
		||||
import { ApiError } from '../../error.js';
 | 
			
		||||
 | 
			
		||||
export const meta = {
 | 
			
		||||
| 
						 | 
				
			
			@ -152,6 +153,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 | 
			
		|||
		private accountUpdateService: AccountUpdateService,
 | 
			
		||||
		private hashtagService: HashtagService,
 | 
			
		||||
		private roleService: RoleService,
 | 
			
		||||
		private cacheService: CacheService,
 | 
			
		||||
	) {
 | 
			
		||||
		super(meta, paramDef, async (ps, _user, token) => {
 | 
			
		||||
			const user = await this.usersRepository.findOneByOrFail({ id: _user.id });
 | 
			
		||||
| 
						 | 
				
			
			@ -276,9 +278,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 | 
			
		|||
				includeSecrets: isSecure,
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			const updatedProfile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id });
 | 
			
		||||
 | 
			
		||||
			this.cacheService.userProfileCache.set(user.id, updatedProfile);
 | 
			
		||||
 | 
			
		||||
			// Publish meUpdated event
 | 
			
		||||
			this.globalEventService.publishMainStream(user.id, 'meUpdated', iObj);
 | 
			
		||||
			this.globalEventService.publishUserEvent(user.id, 'updateUserProfile', await this.userProfilesRepository.findOneByOrFail({ userId: user.id }));
 | 
			
		||||
			this.globalEventService.publishUserEvent(user.id, 'updateUserProfile', updatedProfile);
 | 
			
		||||
 | 
			
		||||
			// 鍵垢を解除したとき、溜まっていたフォローリクエストがあるならすべて承認
 | 
			
		||||
			if (user.isLocked && ps.isLocked === false) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,6 +7,7 @@ import type { Muting } from '@/models/entities/Muting.js';
 | 
			
		|||
import { GlobalEventService } from '@/core/GlobalEventService.js';
 | 
			
		||||
import { DI } from '@/di-symbols.js';
 | 
			
		||||
import { GetterService } from '@/server/api/GetterService.js';
 | 
			
		||||
import { CacheService } from '@/core/CacheService.js';
 | 
			
		||||
import { ApiError } from '../../error.js';
 | 
			
		||||
 | 
			
		||||
export const meta = {
 | 
			
		||||
| 
						 | 
				
			
			@ -65,6 +66,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 | 
			
		|||
		private globalEventService: GlobalEventService,
 | 
			
		||||
		private getterService: GetterService,
 | 
			
		||||
		private idService: IdService,
 | 
			
		||||
		private cacheService: CacheService,
 | 
			
		||||
	) {
 | 
			
		||||
		super(meta, paramDef, async (ps, me) => {
 | 
			
		||||
			const muter = me;
 | 
			
		||||
| 
						 | 
				
			
			@ -103,6 +105,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 | 
			
		|||
				muteeId: mutee.id,
 | 
			
		||||
			} as Muting);
 | 
			
		||||
 | 
			
		||||
			this.cacheService.userMutingsCache.delete(muter.id);
 | 
			
		||||
			this.globalEventService.publishUserEvent(me.id, 'mute', mutee);
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -92,8 +92,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 | 
			
		|||
				muterId: muter.id,
 | 
			
		||||
				muteeId: mutee.id,
 | 
			
		||||
			} as RenoteMuting);
 | 
			
		||||
 | 
			
		||||
		// publishUserEvent(user.id, 'mute', mutee);
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,7 @@ import type { EventEmitter } from 'events';
 | 
			
		|||
//#region Stream type-body definitions
 | 
			
		||||
export interface InternalStreamTypes {
 | 
			
		||||
	userChangeSuspendedState: { id: User['id']; isSuspended: User['isSuspended']; };
 | 
			
		||||
	userTokenRegenerated: { id: User['id']; oldToken: User['token']; newToken: User['token']; };
 | 
			
		||||
	userTokenRegenerated: { id: User['id']; oldToken: string; newToken: string; };
 | 
			
		||||
	remoteUserUpdated: { id: User['id']; };
 | 
			
		||||
	follow: { followerId: User['id']; followeeId: User['id']; };
 | 
			
		||||
	unfollow: { followerId: User['id']; followeeId: User['id']; };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,7 @@ import type { Role, RolesRepository, RoleAssignmentsRepository, UsersRepository,
 | 
			
		|||
import { DI } from '@/di-symbols.js';
 | 
			
		||||
import { MetaService } from '@/core/MetaService.js';
 | 
			
		||||
import { genAid } from '@/misc/id/aid.js';
 | 
			
		||||
import { UserCacheService } from '@/core/UserCacheService.js';
 | 
			
		||||
import { CacheService } from '@/core/CacheService.js';
 | 
			
		||||
import { IdService } from '@/core/IdService.js';
 | 
			
		||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
 | 
			
		||||
import { sleep } from '../utils.js';
 | 
			
		||||
| 
						 | 
				
			
			@ -65,7 +65,7 @@ describe('RoleService', () => {
 | 
			
		|||
			],
 | 
			
		||||
			providers: [
 | 
			
		||||
				RoleService,
 | 
			
		||||
				UserCacheService,
 | 
			
		||||
				CacheService,
 | 
			
		||||
				IdService,
 | 
			
		||||
				GlobalEventService,
 | 
			
		||||
			],
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue