Skip to content

Commit e98c70b

Browse files
fix: high cpu usage with large amount of channels (#37522)
Co-authored-by: Matheus Cardoso <[email protected]>
1 parent b55bb42 commit e98c70b

File tree

2 files changed

+38
-20
lines changed

2 files changed

+38
-20
lines changed

.changeset/quiet-cars-smile.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@rocket.chat/meteor": patch
3+
---
4+
5+
Fixes client slowdown for users with large amount of channels

apps/meteor/client/views/root/hooks/loggedIn/useUnread.ts

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import { manageFavicon } from '@rocket.chat/favicon';
22
import { useSession, useSessionDispatch, useUserPreference, useUserSubscriptions } from '@rocket.chat/ui-contexts';
3-
import { useEffect } from 'react';
3+
import { useEffect, useRef } from 'react';
44

55
import { useFireGlobalEvent } from '../../../../hooks/useFireGlobalEvent';
66

77
const query = { open: { $ne: false }, hideUnreadStatus: { $ne: true }, archived: { $ne: true } };
88
const options = { fields: { unread: 1, alert: 1, rid: 1, t: 1, name: 1, ls: 1, unreadAlert: 1, fname: 1, prid: 1 } };
99
const updateFavicon = manageFavicon();
1010

11+
type UnreadData = { unread: number; alert: boolean | undefined; unreadAlert: string | undefined };
12+
1113
export const useUnread = () => {
1214
const unreadAlertEnabled = useUserPreference('unreadAlert');
1315
const setUnread = useSessionDispatch('unread');
@@ -18,37 +20,48 @@ export const useUnread = () => {
1820

1921
const subscriptions = useUserSubscriptions(query, options);
2022

23+
// We keep a lightweight snapshot of the last emitted per-subscription unread state so we only
24+
// fire "unread-changed-by-subscription" for subscriptions whose unread-relevant fields changed.
25+
// Previously we emitted one global event per subscription on ANY change, which scaled O(N)
26+
// with the user subscription count (thousands) for every single message event, dominating CPU.
27+
const prevSubsRef = useRef(new Map<string, UnreadData>());
28+
2129
useEffect(() => {
22-
let unreadAlert: false | '•' = false;
30+
let badgeIndicator: false | '•' = false;
31+
let unreadCount = 0;
32+
const nextSnapshot = new Map<string, UnreadData>();
2333

24-
const unreadCount = subscriptions.reduce((ret, subscription) => {
25-
fireEventUnreadChangedBySubscription(subscription);
34+
for (const subscription of subscriptions) {
35+
const { rid, unread: unreadValue, alert, unreadAlert: subscriptionUnreadAlert } = subscription;
36+
const prev = prevSubsRef.current.get(rid);
37+
// Emit per-sub event only if something that influences unread UI changed.
38+
if (!prev || prev.unread !== unreadValue || prev.alert !== alert || prev.unreadAlert !== subscriptionUnreadAlert) {
39+
fireEventUnreadChangedBySubscription(subscription);
40+
}
41+
nextSnapshot.set(rid, { unread: unreadValue, alert, unreadAlert: subscriptionUnreadAlert });
2642

27-
if (subscription.alert || subscription.unread > 0) {
28-
// Increment the total unread count.
29-
if (subscription.alert === true && subscription.unreadAlert !== 'nothing') {
30-
if (subscription.unreadAlert === 'all' || unreadAlertEnabled !== false) {
31-
unreadAlert = '•';
43+
if (alert || unreadValue > 0) {
44+
if (alert === true && subscriptionUnreadAlert !== 'nothing') {
45+
if (subscriptionUnreadAlert === 'all' || unreadAlertEnabled !== false) {
46+
badgeIndicator = '•';
3247
}
3348
}
34-
return ret + subscription.unread;
49+
unreadCount += unreadValue;
3550
}
36-
return ret;
37-
}, 0);
51+
}
52+
53+
prevSubsRef.current = nextSnapshot; // swap snapshot
3854

3955
if (unreadCount > 0) {
40-
if (unreadCount > 999) {
41-
setUnread('999+');
42-
} else {
43-
setUnread(unreadCount);
44-
}
45-
} else if (unreadAlert !== false) {
46-
setUnread(unreadAlert);
56+
setUnread(unreadCount > 999 ? '999+' : unreadCount);
57+
} else if (badgeIndicator !== false) {
58+
setUnread(badgeIndicator);
4759
} else {
4860
setUnread('');
4961
}
62+
5063
fireEventUnreadChanged(unreadCount);
51-
}, [setUnread, unread, subscriptions, unreadAlertEnabled, fireEventUnreadChangedBySubscription, fireEventUnreadChanged]);
64+
}, [setUnread, subscriptions, unreadAlertEnabled, fireEventUnreadChangedBySubscription, fireEventUnreadChanged]);
5265

5366
useEffect(() => {
5467
updateFavicon(unread);

0 commit comments

Comments
 (0)