From 2814a8121ebcc50e2a75e30afd58c28792715023 Mon Sep 17 00:00:00 2001 From: Harxhit Date: Mon, 9 Mar 2026 20:49:02 +0530 Subject: [PATCH 1/3] refactor(server): optimize addAllUserToRoom cursor iteration --- apps/meteor/server/methods/addAllUserToRoom.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/apps/meteor/server/methods/addAllUserToRoom.ts b/apps/meteor/server/methods/addAllUserToRoom.ts index 57f9ea3600b39..199a89ae883f4 100644 --- a/apps/meteor/server/methods/addAllUserToRoom.ts +++ b/apps/meteor/server/methods/addAllUserToRoom.ts @@ -36,13 +36,14 @@ export const addAllUserToRoomFn = async (userId: string, rid: IRoom['_id'], acti if (activeUsersOnly === true) { userFilter.active = true; } + const count = await Users.countDocuments(userFilter); - const users = await Users.find(userFilter).toArray(); - if (users.length > settings.get('API_User_Limit')) { + if (count > settings.get('API_User_Limit')) { throw new Meteor.Error('error-user-limit-exceeded', 'User Limit Exceeded', { method: 'addAllToRoom', }); } + const usersCursor = Users.find(userFilter).batchSize(100); const room = await Rooms.findOneById(rid); if (!room) { @@ -50,11 +51,15 @@ export const addAllUserToRoomFn = async (userId: string, rid: IRoom['_id'], acti method: 'addAllToRoom', }); } + const usernames: string[] = []; + for await (const user of usersCursor) { + if (user.username) { + usernames.push(user.username); + } + } + await beforeAddUserToRoom(usernames, room); - await beforeAddUserToRoom( - users.map((u) => u.username!), - room, - ); + const users = Users.find(userFilter).batchSize(100); const now = new Date(); for await (const user of users) { From fff88fb864fb6728f1a12353a1cbaa87f163bab7 Mon Sep 17 00:00:00 2001 From: Harxhit Date: Tue, 10 Mar 2026 00:04:12 +0530 Subject: [PATCH 2/3] refactor(server): use single cursor pass in addAllUserToRoom to avoid race condition --- .../meteor/server/methods/addAllUserToRoom.ts | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/apps/meteor/server/methods/addAllUserToRoom.ts b/apps/meteor/server/methods/addAllUserToRoom.ts index 199a89ae883f4..24885ce4d1790 100644 --- a/apps/meteor/server/methods/addAllUserToRoom.ts +++ b/apps/meteor/server/methods/addAllUserToRoom.ts @@ -12,6 +12,7 @@ import { settings } from '../../app/settings/server'; import { getDefaultSubscriptionPref } from '../../app/utils/lib/getDefaultSubscriptionPref'; import { callbacks } from '../lib/callbacks'; import { getSubscriptionAutotranslateDefaultConfig } from '../lib/getSubscriptionAutotranslateDefaultConfig'; +import type { IUser } from '@rocket.chat/core-typings'; declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -36,35 +37,39 @@ export const addAllUserToRoomFn = async (userId: string, rid: IRoom['_id'], acti if (activeUsersOnly === true) { userFilter.active = true; } - const count = await Users.countDocuments(userFilter); - - if (count > settings.get('API_User_Limit')) { - throw new Meteor.Error('error-user-limit-exceeded', 'User Limit Exceeded', { + + const room = await Rooms.findOneById(rid); + if (!room) { + throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'addAllToRoom', }); } - const usersCursor = Users.find(userFilter).batchSize(100); - const room = await Rooms.findOneById(rid); - if (!room) { - throw new Meteor.Error('error-invalid-room', 'Invalid room', { + const count = await Users.countDocuments(userFilter); + if (count > settings.get('API_User_Limit')) { + throw new Meteor.Error('error-user-limit-exceeded', 'User Limit Exceeded', { method: 'addAllToRoom', }); } + const usersCursor = Users.find(userFilter).batchSize(100) + + const collectedUsers : IUser[] = []; const usernames: string[] = []; - for await (const user of usersCursor) { - if (user.username) { - usernames.push(user.username); + for await(const user of usersCursor){ + collectedUsers.push(user); + if(user.username){ + usernames.push(user.username) } } - await beforeAddUserToRoom(usernames, room); - - const users = Users.find(userFilter).batchSize(100); + await beforeAddUserToRoom( + usernames, + room + ); const now = new Date(); - for await (const user of users) { + for (const user of collectedUsers) { const subscription = await Subscriptions.findOneByRoomIdAndUserId(rid, user._id); - if (subscription != null) { + if (subscription) { continue; } await callbacks.run('beforeJoinRoom', user, room); From a8ee26f813bc7be41509e6b4da181c89db6f2a44 Mon Sep 17 00:00:00 2001 From: Harxhit Date: Tue, 10 Mar 2026 00:18:34 +0530 Subject: [PATCH 3/3] style(server): consolidate IUser import --- apps/meteor/server/methods/addAllUserToRoom.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/meteor/server/methods/addAllUserToRoom.ts b/apps/meteor/server/methods/addAllUserToRoom.ts index 24885ce4d1790..642370581d3ae 100644 --- a/apps/meteor/server/methods/addAllUserToRoom.ts +++ b/apps/meteor/server/methods/addAllUserToRoom.ts @@ -1,5 +1,5 @@ import { Message } from '@rocket.chat/core-services'; -import type { IRoom } from '@rocket.chat/core-typings'; +import type { IRoom, IUser } from '@rocket.chat/core-typings'; import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Subscriptions, Rooms, Users } from '@rocket.chat/models'; import { check } from 'meteor/check'; @@ -12,7 +12,6 @@ import { settings } from '../../app/settings/server'; import { getDefaultSubscriptionPref } from '../../app/utils/lib/getDefaultSubscriptionPref'; import { callbacks } from '../lib/callbacks'; import { getSubscriptionAutotranslateDefaultConfig } from '../lib/getSubscriptionAutotranslateDefaultConfig'; -import type { IUser } from '@rocket.chat/core-typings'; declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -53,7 +52,7 @@ export const addAllUserToRoomFn = async (userId: string, rid: IRoom['_id'], acti } const usersCursor = Users.find(userFilter).batchSize(100) - const collectedUsers : IUser[] = []; + const collectedUsers: IUser[] = []; const usernames: string[] = []; for await(const user of usersCursor){ collectedUsers.push(user);