Skip to content

Commit 0bfcda1

Browse files
committed
update ws partial
1 parent ebaac1f commit 0bfcda1

3 files changed

Lines changed: 147 additions & 22 deletions

File tree

src/cache/UserCache.ts

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { redisClient } from '../common/redis';
33
import { UserStatus } from '../types/User';
44
import { getSuspensionDetails, getUserWithAccount, isIpBanned } from '../services/User/User';
55
import { USER_CACHE_KEY_STRING, ALLOWED_IP_KEY_SET, CONNECTED_SOCKET_ID_KEY_SET, CONNECTED_USER_ID_KEY_STRING, GOOGLE_DRIVE_ACCESS_TOKEN, USER_PRESENCE_KEY_STRING, SESSION_ID_TO_USER_ID } from './CacheKeys';
6-
import { prisma } from '../common/database';
6+
import { prisma, publicUserExcludeFields } from '../common/database';
77
import { generateId } from '../common/flakeId';
88
import { removeDuplicates } from '../common/utils';
99
import { hasBit, USER_BADGES } from '../common/Bitwise';
@@ -38,6 +38,100 @@ interface GetUserPresencesOpts {
3838
limitActivities?: boolean;
3939
}
4040

41+
export const getServerMembersOnline = async (userId: string, serverIds: string[], currentServerId?: string) => {
42+
const selfMembers = await prisma.serverMember.findMany({
43+
where: {
44+
serverId: { in: serverIds },
45+
userId,
46+
},
47+
include: {
48+
user: {
49+
select: {
50+
...publicUserExcludeFields,
51+
profile: { select: { font: true, clan: { select: { tag: true, icon: true, serverId: true } } } },
52+
lastOnlineAt: true,
53+
lastOnlineStatus: true,
54+
},
55+
},
56+
},
57+
});
58+
59+
const processedUserIds = new Set<string>();
60+
const membersList = [];
61+
62+
if (currentServerId) {
63+
const currentServerMembers = await prisma.serverMember.findMany({
64+
where: {
65+
serverId: currentServerId,
66+
userId: { not: userId },
67+
},
68+
include: {
69+
user: {
70+
select: {
71+
...publicUserExcludeFields,
72+
profile: { select: { font: true, clan: { select: { tag: true, icon: true, serverId: true } } } },
73+
lastOnlineAt: true,
74+
lastOnlineStatus: true,
75+
},
76+
},
77+
},
78+
});
79+
80+
membersList.push(...currentServerMembers);
81+
82+
for (const member of currentServerMembers) {
83+
processedUserIds.add(member.userId);
84+
}
85+
}
86+
87+
const backgroundServerIds = currentServerId ? serverIds.filter((id) => id !== currentServerId) : serverIds;
88+
89+
if (!backgroundServerIds.length) {
90+
return [...selfMembers, ...membersList];
91+
}
92+
93+
let cursor = 0;
94+
95+
do {
96+
const { cursor: nextCursor, keys } = await redisClient.scan(cursor, {
97+
MATCH: USER_PRESENCE_KEY_STRING('*'),
98+
COUNT: 500,
99+
});
100+
cursor = Number(nextCursor);
101+
102+
if (!keys.length) continue;
103+
104+
const onlineUserIds = keys.map((key) => key.replace('user:presence:', '')).filter((id) => id !== userId && !processedUserIds.has(id));
105+
106+
if (!onlineUserIds.length) continue;
107+
108+
for (const id of onlineUserIds) {
109+
processedUserIds.add(id);
110+
}
111+
112+
const batch = await prisma.serverMember.findMany({
113+
where: {
114+
serverId: { in: backgroundServerIds },
115+
userId: { in: onlineUserIds },
116+
},
117+
include: {
118+
user: {
119+
select: {
120+
...publicUserExcludeFields,
121+
profile: { select: { font: true, clan: { select: { tag: true, icon: true, serverId: true } } } },
122+
lastOnlineAt: true,
123+
lastOnlineStatus: true,
124+
},
125+
},
126+
},
127+
});
128+
129+
membersList.push(...batch);
130+
} while (cursor !== 0);
131+
132+
return [...selfMembers, ...membersList];
133+
};
134+
41135
export async function getUserPresences(opts: GetUserPresencesOpts): Promise<Presence[]> {
42136
const userIds = opts.userIds;
43137
const includeSocketId = opts.includeSocketId === undefined ? false : opts.includeSocketId;

src/services/Server.ts

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Channel, Prisma, Server, ServerRole } from '@src/generated/prisma/client';
2-
import { getUserPresences } from '../cache/UserCache';
2+
import { getServerMembersOnline, getUserPresences } from '../cache/UserCache';
33
import { CustomResult } from '../common/CustomResult';
44
import { dateToDateTime, exists, prisma, publicUserExcludeFields, removeServerIdFromAccountOrder } from '../common/database';
55
import { CustomError, generateError } from '../common/errorHandler';
@@ -149,7 +149,7 @@ export const createServer = async (opts: CreateServerOptions): Promise<CustomRes
149149
return [server, null];
150150
};
151151

152-
export const getServers = async (userId: string) => {
152+
export const getServers = async (userId: string, partial?: boolean, currentServerId?: string) => {
153153
const user = await prisma.user.findUnique({
154154
where: { id: userId },
155155
include: {
@@ -166,9 +166,20 @@ export const getServers = async (userId: string) => {
166166
where: { deleting: null },
167167
include: { _count: { select: { attachments: true } }, permissions: { select: { permissions: true, roleId: true } } },
168168
},
169-
serverMembers: {
170-
include: { user: { select: { ...publicUserExcludeFields, profile: { select: { font: true, clan: { select: { tag: true, icon: true, serverId: true } } } }, lastOnlineAt: true, lastOnlineStatus: true } } },
171-
},
169+
serverMembers: partial
170+
? false
171+
: {
172+
include: {
173+
user: {
174+
select: {
175+
...publicUserExcludeFields,
176+
profile: { select: { font: true, clan: { select: { tag: true, icon: true, serverId: true } } } },
177+
lastOnlineAt: true,
178+
lastOnlineStatus: true,
179+
},
180+
},
181+
},
182+
},
172183
roles: true,
173184
customEmojis: {
174185
select: {
@@ -183,21 +194,41 @@ export const getServers = async (userId: string) => {
183194
},
184195
});
185196
const servers = user?.servers || [];
197+
const serverIds = servers.map((s) => s.id);
186198
let serverChannels: Channel[] = [];
187199
let serverMembers: ServerMemberWithLastOnlineDetails[] = [];
188200

189201
let serverRoles: ServerRole[] = [];
190202

191203
for (let i = 0; i < servers.length; i++) {
192204
const server = servers[i]!;
193-
const updatedServerMembers = filterLastOnlineDetailsFromServerMembers(server.serverMembers, userId);
194-
server.serverMembers = updatedServerMembers;
195-
196205
serverChannels = [...serverChannels, ...server.channels];
197-
serverMembers = [...serverMembers, ...server.serverMembers];
198206
serverRoles = [...serverRoles, ...server.roles];
199207
}
200208

209+
if (!serverIds.includes(currentServerId!)) {
210+
currentServerId = undefined;
211+
}
212+
213+
if (partial) {
214+
serverMembers = await getServerMembersOnline(userId, serverIds, currentServerId);
215+
216+
for (let i = 0; i < servers.length; i++) {
217+
const server = servers[i]!;
218+
server.serverMembers = serverMembers.filter((m) => m.serverId === server.id);
219+
}
220+
} else {
221+
for (let i = 0; i < servers.length; i++) {
222+
const server = servers[i]!;
223+
224+
if (server.serverMembers) {
225+
const updatedServerMembers = filterLastOnlineDetailsFromServerMembers(server.serverMembers as any, userId);
226+
server.serverMembers = updatedServerMembers;
227+
serverMembers = [...serverMembers, ...(server.serverMembers as any[])];
228+
}
229+
}
230+
}
231+
201232
return {
202233
servers: user?.servers || [],
203234
serverChannels,

src/socket/events/onAuthenticate.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,12 @@ const handleAuthenticate = async (socket: Socket, payload: Payload) => {
138138
emitError(socket, { message: 'User not found.', disconnect: true });
139139
return;
140140
}
141-
const { servers, serverChannels, serverMembers, serverRoles } = await getServers(userCache.id);
141+
142+
const t1 = performance.now();
143+
const { servers, serverChannels, serverMembers, serverRoles } = await getServers(userCache.id, payload.partial, payload.currentServerId);
144+
if (user.id === '1289157673362825217') {
145+
console.log('getServers took', performance.now() - t1, 'ms');
146+
}
142147

143148
const lastSeenServerChannelIds = await getLastSeenServerChannelIdsByUserId(userCache.id);
144149

@@ -257,17 +262,12 @@ const handleAuthenticate = async (socket: Socket, payload: Payload) => {
257262
let validCurrentServerId = false;
258263
const serverMembersToEmit = () => {
259264
if (!payload.partial) return serverMembers;
260-
return serverMembers.filter((member) => {
261-
if (payload.currentServerId === member.serverId) {
262-
markMembersFetched(socket.id, member.serverId);
263-
validCurrentServerId = true;
264-
return true;
265-
}
266-
if (presences.find((presence) => presence.userId === member.user.id)) {
267-
return true;
268-
}
269-
return member.user.id === userCache.id;
270-
});
265+
if (payload.currentServerId) {
266+
markMembersFetched(socket.id, payload.currentServerId);
267+
validCurrentServerId = true;
268+
return true;
269+
}
270+
return serverMembers;
271271
};
272272

273273
const data = {

0 commit comments

Comments
 (0)