diff options
| author | Hazelnoot <acomputerdog@gmail.com> | 2025-06-08 19:52:59 -0400 |
|---|---|---|
| committer | Hazelnoot <acomputerdog@gmail.com> | 2025-06-09 11:02:51 -0400 |
| commit | fa68751a19877474bf78a80ef7204102296f0f17 (patch) | |
| tree | 63d81dbc815f0d7c07a7f7effb51db026e1d8121 /packages/backend/src/core/entities | |
| parent | implement userFollowersCache (diff) | |
| download | sharkey-fa68751a19877474bf78a80ef7204102296f0f17.tar.gz sharkey-fa68751a19877474bf78a80ef7204102296f0f17.tar.bz2 sharkey-fa68751a19877474bf78a80ef7204102296f0f17.zip | |
normalize userFollowingsCache / userFollowersCache and add hibernatedUserCache to reduce the number of cache-clears and allow use of caching in many more places
Diffstat (limited to 'packages/backend/src/core/entities')
| -rw-r--r-- | packages/backend/src/core/entities/NoteEntityService.ts | 10 | ||||
| -rw-r--r-- | packages/backend/src/core/entities/UserEntityService.ts | 44 |
2 files changed, 17 insertions, 37 deletions
diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index 3af66b220d..9b447a4064 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -11,7 +11,7 @@ import type { Packed } from '@/misc/json-schema.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { MiUser } from '@/models/User.js'; import type { MiNote } from '@/models/Note.js'; -import type { UsersRepository, NotesRepository, FollowingsRepository, PollsRepository, PollVotesRepository, NoteReactionsRepository, ChannelsRepository, MiMeta, MiPollVote, MiPoll, MiChannel } from '@/models/_.js'; +import type { UsersRepository, NotesRepository, FollowingsRepository, PollsRepository, PollVotesRepository, NoteReactionsRepository, ChannelsRepository, MiMeta, MiPollVote, MiPoll, MiChannel, MiFollowing } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; import { DebounceLoader } from '@/misc/loader.js'; import { IdService } from '@/core/IdService.js'; @@ -133,7 +133,7 @@ export class NoteEntityService implements OnModuleInit { @bindThis public async hideNote(packedNote: Packed<'Note'>, meId: MiUser['id'] | null, hint?: { - myFollowing?: ReadonlyMap<string, { withReplies: boolean }>, + myFollowing?: ReadonlyMap<string, unknown>, myBlockers?: ReadonlySet<string>, }): Promise<void> { if (meId === packedNote.userId) return; @@ -416,7 +416,7 @@ export class NoteEntityService implements OnModuleInit { packedFiles: Map<MiNote['fileIds'][number], Packed<'DriveFile'> | null>; packedUsers: Map<MiUser['id'], Packed<'UserLite'>>; mentionHandles: Record<string, string | undefined>; - userFollowings: Map<string, Map<string, { withReplies: boolean }>>; + userFollowings: Map<string, Map<string, Omit<MiFollowing, 'isFollowerHibernated'>>>; userBlockers: Map<string, Set<string>>; polls: Map<string, MiPoll>; pollVotes: Map<string, Map<string, MiPollVote[]>>; @@ -659,9 +659,9 @@ export class NoteEntityService implements OnModuleInit { // mentionHandles this.getUserHandles(Array.from(mentionedUsers)), // userFollowings - this.cacheService.getUserFollowings(userIds), + this.cacheService.userFollowingsCache.fetchMany(userIds).then(fs => new Map(fs)), // userBlockers - this.cacheService.getUserBlockers(userIds), + this.cacheService.userBlockedCache.fetchMany(userIds).then(bs => new Map(bs)), // polls this.pollsRepository.findBy({ noteId: In(noteIds) }) .then(polls => new Map(polls.map(p => [p.noteId, p]))), diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 8ed482af6f..aecbaa7fd5 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -79,7 +79,7 @@ function isRemoteUser(user: MiUser | { host: MiUser['host'] }): boolean { export type UserRelation = { id: MiUser['id'] - following: MiFollowing | null, + following: Omit<MiFollowing, 'isFollowerHibernated'> | null, isFollowing: boolean isFollowed: boolean hasPendingFollowRequestFromYou: boolean @@ -197,16 +197,8 @@ export class UserEntityService implements OnModuleInit { memo, mutedInstances, ] = await Promise.all([ - this.followingsRepository.findOneBy({ - followerId: me, - followeeId: target, - }), - this.followingsRepository.exists({ - where: { - followerId: target, - followeeId: me, - }, - }), + this.cacheService.userFollowingsCache.fetch(me).then(f => f.get(target) ?? null), + this.cacheService.userFollowingsCache.fetch(target).then(f => f.has(me)), this.followRequestsRepository.exists({ where: { followerId: me, @@ -227,8 +219,7 @@ export class UserEntityService implements OnModuleInit { .then(mutings => mutings.has(target)), this.cacheService.renoteMutingsCache.fetch(me) .then(mutings => mutings.has(target)), - this.cacheService.userByIdCache.fetch(target, () => this.usersRepository.findOneByOrFail({ id: target })) - .then(user => user.host), + this.cacheService.findUserById(target).then(u => u.host), this.userMemosRepository.createQueryBuilder('m') .select('m.memo') .where({ userId: me, targetUserId: target }) @@ -271,13 +262,8 @@ export class UserEntityService implements OnModuleInit { memos, mutedInstances, ] = await Promise.all([ - this.followingsRepository.findBy({ followerId: me }) - .then(f => new Map(f.map(it => [it.followeeId, it]))), - this.followingsRepository.createQueryBuilder('f') - .select('f.followerId') - .where('f.followeeId = :me', { me }) - .getRawMany<{ f_followerId: string }>() - .then(it => it.map(it => it.f_followerId)), + this.cacheService.userFollowingsCache.fetch(me), + this.cacheService.userFollowersCache.fetch(me), this.followRequestsRepository.createQueryBuilder('f') .select('f.followeeId') .where('f.followerId = :me', { me }) @@ -322,7 +308,7 @@ export class UserEntityService implements OnModuleInit { id: target, following: following, isFollowing: following != null, - isFollowed: followees.includes(target), + isFollowed: followees.has(target), hasPendingFollowRequestFromYou: followersRequests.includes(target), hasPendingFollowRequestToYou: followeesRequests.includes(target), isBlocking: blockees.has(target), @@ -354,7 +340,7 @@ export class UserEntityService implements OnModuleInit { return false; // TODO } - // TODO make redis calls in MULTI? + // TODO optimization: make redis calls in MULTI @bindThis public async getNotificationsInfo(userId: MiUser['id']): Promise<{ hasUnread: boolean; @@ -789,11 +775,11 @@ export class UserEntityService implements OnModuleInit { .map(user => user.host) .filter((host): host is string => host != null)); - const _profilesFromUsers: MiUserProfile[] = []; + const _profilesFromUsers: [string, MiUserProfile][] = []; const _profilesToFetch: string[] = []; for (const user of _users) { if (user.userProfile) { - _profilesFromUsers.push(user.userProfile); + _profilesFromUsers.push([user.id, user.userProfile]); } else { _profilesToFetch.push(user.id); } @@ -803,13 +789,7 @@ export class UserEntityService implements OnModuleInit { const [profilesMap, userMemos, userRelations, pinNotes, userIdsByUri, instances, securityKeyCounts, pendingReceivedFollows, pendingSentFollows] = await Promise.all([ // profilesMap - this.cacheService.getUserProfiles(_profilesToFetch) - .then(profiles => { - for (const profile of _profilesFromUsers) { - profiles.set(profile.userId, profile); - } - return profiles; - }), + this.cacheService.userProfileCache.fetchMany(_profilesToFetch).then(profiles => new Map(profiles.concat(_profilesFromUsers))), // userMemos isDetailed && meId ? this.userMemosRepository.findBy({ userId: meId }) .then(memos => new Map(memos.map(memo => [memo.targetUserId, memo.memo]))) : new Map(), @@ -857,7 +837,7 @@ export class UserEntityService implements OnModuleInit { .groupBy('key.userId') .getRawMany<{ userId: string, userCount: number }>() .then(counts => new Map(counts.map(c => [c.userId, c.userCount]))) : new Map(), - // TODO check query performance + // TODO optimization: cache follow requests // pendingReceivedFollows isDetailedAndMe ? this.followRequestsRepository.createQueryBuilder('req') .select('req.followeeId', 'followeeId') |