summaryrefslogtreecommitdiff
path: root/packages/backend/src/core/entities
diff options
context:
space:
mode:
authorHazelnoot <acomputerdog@gmail.com>2025-06-08 19:52:59 -0400
committerHazelnoot <acomputerdog@gmail.com>2025-06-09 11:02:51 -0400
commitfa68751a19877474bf78a80ef7204102296f0f17 (patch)
tree63d81dbc815f0d7c07a7f7effb51db026e1d8121 /packages/backend/src/core/entities
parentimplement userFollowersCache (diff)
downloadsharkey-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.ts10
-rw-r--r--packages/backend/src/core/entities/UserEntityService.ts44
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')