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
75 lines
1.6 KiB
JavaScript
75 lines
1.6 KiB
JavaScript
import types from '../mutation-types';
|
|
import ConversationApi from '../../api/inbox/conversation';
|
|
import { debounce } from '@chatwoot/utils';
|
|
|
|
const state = {
|
|
mineCount: 0,
|
|
unAssignedCount: 0,
|
|
allCount: 0,
|
|
};
|
|
|
|
export const getters = {
|
|
getStats: $state => $state,
|
|
};
|
|
|
|
// Create a debounced version of the actual API call function
|
|
const fetchMetaData = async (commit, params) => {
|
|
try {
|
|
const response = await ConversationApi.meta(params);
|
|
const {
|
|
data: { meta },
|
|
} = response;
|
|
commit(types.SET_CONV_TAB_META, meta);
|
|
} catch (error) {
|
|
// ignore
|
|
}
|
|
};
|
|
|
|
const debouncedFetchMetaData = debounce(fetchMetaData, 500, false, 2000);
|
|
const longDebouncedFetchMetaData = debounce(fetchMetaData, 5000, false, 10000);
|
|
const superLongDebouncedFetchMetaData = debounce(
|
|
fetchMetaData,
|
|
10000,
|
|
false,
|
|
20000
|
|
);
|
|
|
|
export const actions = {
|
|
get: async ({ commit, state: $state }, params) => {
|
|
if ($state.allCount > 2000) {
|
|
superLongDebouncedFetchMetaData(commit, params);
|
|
} else if ($state.allCount > 100) {
|
|
longDebouncedFetchMetaData(commit, params);
|
|
} else {
|
|
debouncedFetchMetaData(commit, params);
|
|
}
|
|
},
|
|
set({ commit }, meta) {
|
|
commit(types.SET_CONV_TAB_META, meta);
|
|
},
|
|
};
|
|
|
|
export const mutations = {
|
|
[types.SET_CONV_TAB_META](
|
|
$state,
|
|
{
|
|
mine_count: mineCount,
|
|
unassigned_count: unAssignedCount,
|
|
all_count: allCount,
|
|
} = {}
|
|
) {
|
|
$state.mineCount = mineCount;
|
|
$state.allCount = allCount;
|
|
$state.unAssignedCount = unAssignedCount;
|
|
$state.updatedOn = new Date();
|
|
},
|
|
};
|
|
|
|
export default {
|
|
namespaced: true,
|
|
state,
|
|
getters,
|
|
actions,
|
|
mutations,
|
|
};
|