Describe the Bug
While reviewing app/lib/methods/getUsersPresence.ts
I noticed a performance bottleneck and a race condition.
Right now, when the app gets presence updates, it runs db.write() inside an asynchronous users.forEach loop. WatermelonDB explicitly warns against this because it spins up hundreds of concurrent writer processes instead of batching them, which causes massive UI stuttering on slower phones.
Also, the usersBatch array isn't deduplicated. If the app asks for the same user's presence multiple times rapidly, it processes the same ID concurrently. This causes a race condition where both processes try to create() the user at the exact same time, throwing a native SQLite Unique Constraint violation (which ends up as a silent Unhandled Promise Rejection that kills the sync).
Steps to Reproduce
- Open a channel with a lot of active users where presence updates are firing rapidly.
- Rapid [getUserPresence(uid)] calls end up pushing duplicate
uids into the usersBatch array.
- When [getUsersPresence] fires, it attempts to process these duplicates.
- The
users.forEach(async ...) loop spins up dozens of concurrent db.write() operations.
- SQLite Unique Constraint tracking fails for the duplicate IDs, causing an unhandled promise rejection in the background.
- The UI stutters and drops frames heavily due to all the unbatched SQLite writers trying to run at once.
Expected Behavior
- We should deduplicate
usersBatch before processing it (e.g. [...new Set(usersBatch)]).
- We should use WatermelonDB's
Q.oneOf() to bulk-fetch all records at once.
- We should queue up
prepareUpdate and prepareCreate operations and execute them in a single, atomic db.batch() call to fix the crashes and improve performance.
usersBatch allows duplicates.
- Calling
db.write() inside a forEach loop kills performance.
- Concurrent writes for the exact same
uid trigger SQLite unique constraint crashes (Unhandled Promise Rejections).
Actual Behavior
No response
Rocket.Chat Server Version
8.2.0
Rocket.Chat App Version
4.70.0.9999999999
Device Name
Realme 12+
OS Version
Android 15
Additional Context
No response
Describe the Bug
While reviewing app/lib/methods/getUsersPresence.ts
I noticed a performance bottleneck and a race condition.
Right now, when the app gets presence updates, it runs db.write() inside an asynchronous users.forEach loop. WatermelonDB explicitly warns against this because it spins up hundreds of concurrent writer processes instead of batching them, which causes massive UI stuttering on slower phones.
Also, the usersBatch array isn't deduplicated. If the app asks for the same user's presence multiple times rapidly, it processes the same ID concurrently. This causes a race condition where both processes try to create() the user at the exact same time, throwing a native SQLite Unique Constraint violation (which ends up as a silent Unhandled Promise Rejection that kills the sync).
Steps to Reproduce
uids into theusersBatcharray.users.forEach(async ...)loop spins up dozens of concurrentdb.write()operations.Expected Behavior
usersBatchbefore processing it (e.g.[...new Set(usersBatch)]).Q.oneOf()to bulk-fetch all records at once.prepareUpdateandprepareCreateoperations and execute them in a single, atomicdb.batch()call to fix the crashes and improve performance.usersBatchallows duplicates.db.write()inside aforEachloop kills performance.uidtrigger SQLite unique constraint crashes (Unhandled Promise Rejections).Actual Behavior
No response
Rocket.Chat Server Version
8.2.0
Rocket.Chat App Version
4.70.0.9999999999
Device Name
Realme 12+
OS Version
Android 15
Additional Context
No response