On high-volume accounts, the dashboard sidebar's conversation count
badges fall behind because a meaningful share of
`/api/v1/accounts/:id/conversations/meta` requests get rate-limited
(per-user throttle, default 30 req/min).
Root cause is in `conversationStats.js`. The tiered debounce uses
`allCount` from the last response to pick a wait interval. `allCount`
reflects the user's *current filtered scope*, not the account's true
volume — so an agent viewing a small filter on a busy account falls into
the most aggressive tier (500ms wait / 1.5s maxWait → up to 40
calls/min/tab) and trips the throttle.
## What changed
`app/javascript/dashboard/store/modules/conversationStats.js`:
- Short-tier `maxWait`: `1500 → 2000` (caps short-tier at 30/min/tab
instead of 40)
- Super-long-tier threshold: `allCount > 5000 → > 2000` (more
high-volume accounts fall into the safe 3/min/tab tier)
- Middle-tier threshold unchanged (`> 100`)
| Tier (allCount) | wait / maxWait | Calls/min/tab |
|---|---|---|
| `> 2000` | 10s / 20s | 3 |
| `> 100` | 5s / 10s | 6 |
| else | 500ms / 2s | 30 |
## Trade-off
Badge updates (including those triggered by the agent's own action) may
lag by up to the tier's `maxWait` — worst case 20s for accounts with >
2000 open conversations in the active scope. The conversation list
itself and push notifications continue to update in real time; only the
numeric badge is debounced.
## Not in scope
- Sticky-max `allCount` to fix the underlying tier-selection signal —
defer until the simpler tuning is validated in production
- Optimistic count updates on local user actions — adds non-trivial
state management for a cosmetic lag
This PR combines the approaches in
https://github.com/chatwoot/chatwoot/pull/11190 and
https://github.com/chatwoot/chatwoot/pull/11187 to debounce the meta
request with a max wait time of 2.5 seconds With 500 concurrent users,
the theoretical limit with this is 720K requests per minute, if all of
them continuously receive websocket events.
The max wait of 2.5 seconds is still very generous, and we can easily
make it 2 seconds for smaller accounts and 5 seconds for larger
accounts.
```js
const debouncedFetchMetaData = debounce(fetchMetaData, 500, false, 200);
const longDebouncedFetchMetaData = debounce(fetchMetaData, 500, false, 5000);
export const actions = {
get: async ({ commit, state: $state }, params) => {
if ($state.allCount > 100) {
longDebouncedFetchMetaData(commit, params);
} else {
debouncedFetchMetaData(commit, params);
}
},
set({ commit }, meta) {
commit(types.SET_CONV_TAB_META, meta);
},
};
```
Related Utils PR: https://github.com/chatwoot/utils/pull/49
Here's the debounce in action
<img width="934" alt="image"
src="https://github.com/user-attachments/assets/5265a108-9c64-4488-9b4c-2e0d06aadc50"
/>
---------
Co-authored-by: Pranav <pranavrajs@gmail.com>
This update improves the throttling mechanism for conversation meta
requests to optimize server load and enhance performance. The changes
implement differentiated thresholds based on account size - a 2-second
throttle for small accounts (≤100 conversations) and a 10-second
throttle for large accounts (>100 conversations).
Fixes#11178
This PR would reduce the API calls if there are more than 100 total
conversations, we will throttle requests the number of requests.
Earlier this was 1000 total conversations.
For large accounts with huge volumes of messages, it can be very
wasteful to make the meta request so often. It also puts un-necessary
load on the DB bombarding it with so many requests. This PR fixes it by
throttling the requests to 5 seconds for all users with more than 1000
accessible chats.
### Why not cache this value in the backend?
Well, it's a bit tricky, since a user can have different permissions
over inboxes and can see different chats, maintaining a cache for each
of them is not effective, besides the requests will reach the server
anyway.