RocketChat / RocketChat/Rocket.Chat.ReactNative
bug: Performance issue in getUsersPresence due to unbatched WatermelonDB writes and race condition
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 2.4k
- Forks
- 1.5k
- Avg merge
- 1d 18h
- Merged PRs (30d)
- 90
Description
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 theusersBatcharray. - When [getUsersPresence] fires, it attempts to process these duplicates.
- The
users.forEach(async ...)loop spins up dozens of concurrentdb.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
usersBatchbefore 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
prepareUpdateandprepareCreateoperations and execute them in a single, atomicdb.batch()call to fix the crashes and improve performance. usersBatchallows duplicates.- Calling
db.write()inside aforEachloop kills performance. - Concurrent writes for the exact same
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
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with app/lib/methods/getUsersPresence.ts and trace how rapid getUserPresence(uid) calls populate usersBatch. Reproduce the rapid duplicate-presence scenario on the described Android setup; done means duplicate IDs no longer cause SQLite unique-constraint failures, writes are batched, and UI stuttering is reduced.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react-native, sqlite, typescript
- Domain
- database, mobile, performance
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100