From 8ec1ccccfe53c3e870984a6e2dcc593af67414ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 9 Mar 2026 13:29:12 -0400 Subject: [PATCH] fix: promises in groups.leave speed up user.delete user.delete calls `groups.leaveAllGroups` which calls rejectMembership with 500+ groups. This function then tries to remove the user from `group::pending` and `group::invited` sets so a total for 1k sets. You can't be invited or request membership to privilege groups so filter the groups before sending to rejectMembership clearGroupTitleIfSet function tries to remove the group title from the user. It was only skipping privilege groups and registered-users, but unverified-users & verified users weren't added to the check Messaging.leaveRooms, make a single call to isUserInRoom and passing an array of roomIds In user.delete, check utils.isNumber(uid) once. Call deleteVotes/deleteChats/revokeAllSessions in Promise.all If user is local dont call activitypub.actors.remove(), this saves a db call to `await db.isSortedSetMember('usersRemote:lastCrawled', id);` --- src/groups/leave.js | 25 ++++++++++++++--------- src/messaging/rooms.js | 6 ++++-- src/user/delete.js | 46 +++++++++++++++++++++++++----------------- 3 files changed, 47 insertions(+), 30 deletions(-) diff --git a/src/groups/leave.js b/src/groups/leave.js index 32495232c9..f9af8a34ff 100644 --- a/src/groups/leave.js +++ b/src/groups/leave.js @@ -43,20 +43,20 @@ module.exports = function (Groups) { const promises = []; if (emptyPrivilegeGroups.length) { - promises.push(Groups.destroy, emptyPrivilegeGroups); + promises.push(Groups.destroy(emptyPrivilegeGroups)); } if (visibleGroups.length) { promises.push( - db.sortedSetAdd, - 'groups:visible:memberCount', - visibleGroups.map(groupData => groupData.memberCount), - visibleGroups.map(groupData => groupData.name) + db.sortedSetAdd( + 'groups:visible:memberCount', + visibleGroups.map(groupData => groupData.memberCount), + visibleGroups.map(groupData => groupData.name) + ) ); } - await Promise.all(promises); - await Promise.all([ + ...promises, clearGroupTitleIfSet(groupsToLeave, uid), leavePublicRooms(groupsToLeave, uid), ]); @@ -82,7 +82,12 @@ module.exports = function (Groups) { } async function clearGroupTitleIfSet(groupNames, uid) { - groupNames = groupNames.filter(groupName => groupName !== 'registered-users' && !Groups.isPrivilegeGroup(groupName)); + groupNames = groupNames.filter( + groupName => groupName !== 'registered-users' && + groupName !== 'unverified-users' && + groupName !== 'verified-users' && + !Groups.isPrivilegeGroup(groupName) + ); if (!groupNames.length) { return; } @@ -103,7 +108,9 @@ module.exports = function (Groups) { const groups = await db.getSortedSetRange('groups:createtime', 0, -1); await Promise.all([ Groups.leave(groups, uid), - Groups.rejectMembership(groups, uid), + Groups.rejectMembership( + groups.filter(g => !Groups.isPrivilegeGroup(g)), uid + ), ]); }; diff --git a/src/messaging/rooms.js b/src/messaging/rooms.js index 4356775a47..50a252fc0d 100644 --- a/src/messaging/rooms.js +++ b/src/messaging/rooms.js @@ -361,9 +361,11 @@ module.exports = function (Messaging) { }; Messaging.leaveRooms = async (uid, roomIds) => { - const isInRoom = await Promise.all(roomIds.map(roomId => Messaging.isUserInRoom(uid, roomId))); + const isInRoom = await Messaging.isUserInRoom(uid, roomIds); roomIds = roomIds.filter((roomId, index) => isInRoom[index]); - + if (!roomIds.length) { + return; + } const roomKeys = [ ...roomIds.map(roomId => `chat:room:${roomId}:uids`), ...roomIds.map(roomId => `chat:room:${roomId}:owners`), diff --git a/src/user/delete.js b/src/user/delete.js index 17ae717490..12cdbb588d 100644 --- a/src/user/delete.js +++ b/src/user/delete.js @@ -33,10 +33,15 @@ module.exports = function (User) { throw new Error('[[error:already-deleting]]'); } deletesInProgress[uid] = 'user.delete'; - await deletePosts(callerUid, uid); - await deleteTopics(callerUid, uid); - await deleteUploads(callerUid, uid); - await deleteQueued(uid); + async function deletePostsTopics() { + await deletePosts(callerUid, uid); + await deleteTopics(callerUid, uid); + } + await Promise.all([ + deletePostsTopics(), + deleteUploads(callerUid, uid), + deleteQueued(uid), + ]); delete deletesInProgress[uid]; }; @@ -90,9 +95,9 @@ module.exports = function (User) { throw new Error('[[error:already-deleting]]'); } deletesInProgress[uid] = 'user.deleteAccount'; - + const isLocal = utils.isNumber(uid); await removeFromSortedSets(uid); - const userData = await db.getObject(utils.isNumber(uid) ? `user:${uid}` : `userRemote:${uid}`); + const userData = await db.getObject(isLocal ? `user:${uid}` : `userRemote:${uid}`); if (!userData || !userData.username) { delete deletesInProgress[uid]; @@ -100,9 +105,11 @@ module.exports = function (User) { } await plugins.hooks.fire('static:user.delete', { uid: uid, userData: userData }); - await deleteVotes(uid); - await deleteChats(uid); - await User.auth.revokeAllSessions(uid); + await Promise.all([ + deleteVotes(uid), + deleteChats(uid), + User.auth.revokeAllSessions(uid), + ]); const keys = [ `uid:${uid}:notifications:read`, @@ -143,7 +150,7 @@ module.exports = function (User) { await Promise.all([ db.sortedSetRemoveBulk(bulkRemove), - utils.isNumber(uid) ? db.decrObjectField('global', 'userCount') : null, + isLocal ? db.decrObjectField('global', 'userCount') : null, db.deleteAll(keys), db.setRemove('invitation:uids', uid), deleteUserIps(uid), @@ -156,13 +163,13 @@ module.exports = function (User) { flags.resolveFlag('user', uid, uid), User.reset.cleanByUid(uid), User.email.expireValidation(uid), - activitypub.actors.remove(uid), + !isLocal ? activitypub.actors.remove(uid) : null, ]); await db.deleteAll([ `followers:${uid}`, `following:${uid}`, `uid:${uid}:followed_tags`, `uid:${uid}:followed_tids`, `uid:${uid}:ignored_tids`, - `${utils.isNumber(uid) ? 'user' : 'userRemote'}:${uid}`, + `${isLocal ? 'user' : 'userRemote'}:${uid}`, ]); delete deletesInProgress[uid]; return userData; @@ -184,11 +191,10 @@ module.exports = function (User) { } async function deleteVotes(uid) { - const [upvotedPids, downvotedPids] = await Promise.all([ - db.getSortedSetRange(`uid:${uid}:upvote`, 0, -1), - db.getSortedSetRange(`uid:${uid}:downvote`, 0, -1), - ]); - const pids = _.uniq(upvotedPids.concat(downvotedPids).filter(Boolean)); + const upvoteDownvotePids = await db.getSortedSetRange([ + `uid:${uid}:upvote`, `uid:${uid}:downvote`, + ], 0, -1); + const pids = _.uniq(upvoteDownvotePids).filter(Boolean); await async.eachSeries(pids, async (pid) => { await posts.unvote(pid, uid); }); @@ -203,8 +209,10 @@ module.exports = function (User) { async function deleteUserIps(uid) { const ips = await db.getSortedSetRange(`uid:${uid}:ip`, 0, -1); - await db.sortedSetsRemove(ips.map(ip => `ip:${ip}:uid`), uid); - await db.delete(`uid:${uid}:ip`); + await Promise.all([ + db.sortedSetsRemove(ips.map(ip => `ip:${ip}:uid`), uid), + db.delete(`uid:${uid}:ip`), + ]); } async function deleteUserFromFollowers(uid) {