refactor: remove client-side cache key pulls

This commit is contained in:
Shivam Mishra
2026-06-10 12:37:11 +05:30
parent f452b88e8d
commit 0b4d1c801c
6 changed files with 9 additions and 85 deletions
@@ -50,12 +50,11 @@ class CacheEnabledApiClient extends ApiClient {
return this.getFromNetwork();
}
// Trust the IDB cache. Freshness is maintained by:
// - boot-time hydrateStoresFromCache (compares server keys once on boot)
// - ActionCable ACCOUNT_CACHE_INVALIDATED broadcasts (live updates)
// - ReconnectService.revalidateCaches (on WebSocket reconnect)
// Skipping the per-call /cache_keys preflight eliminates N GET requests per
// cold settings-page load.
// Trust the IDB cache. Freshness is maintained by the
// account.cache_invalidated event alone: RoomChannel pushes the cache-key
// map on every (re)subscribe — boot and reconnect included — and the
// server broadcasts it on every change. Skipping a per-call /cache_keys
// preflight eliminates N GET requests per cold settings-page load.
const localData = await this.dataManager.get({
modelName: this.cacheModelName,
});
-7
View File
@@ -9,13 +9,6 @@ class AccountAPI extends ApiClient {
createAccount(data) {
return axios.post(`${this.apiVersion}/accounts`, data);
}
async getCacheKeys() {
const response = await axios.get(
`/api/v1/accounts/${this.accountIdFromRoute}/cache_keys`
);
return response.data.cache_keys;
}
}
export default new AccountAPI();
@@ -6,7 +6,6 @@ import {
isAInboxViewRoute,
isNotificationRoute,
} from 'dashboard/helper/routeHelpers';
import { dispatchCacheRevalidations } from 'dashboard/helper/CacheHelper/dispatchCacheRevalidations';
const MAX_DISCONNECT_SECONDS = 10800;
@@ -99,11 +98,6 @@ class ReconnectService {
await this.store.dispatch('notifications/index', { ...filter, page: 1 });
};
revalidateCaches = async () => {
const keys = (await this.store.dispatch('accounts/getCacheKeys')) || {};
await dispatchCacheRevalidations(this.store, keys);
};
handleRouteSpecificFetch = async () => {
const currentRoute = this.router.currentRoute.value.name;
if (isAConversationRoute(currentRoute, true)) {
@@ -133,9 +127,11 @@ class ReconnectService {
this.setConversationLastMessageId();
};
// Cached workspace config needs no explicit revalidation here: ActionCable
// auto-resubscribes after a drop, and RoomChannel pushes the cache-key map
// on every subscribe via the account.cache_invalidated event.
onReconnect = async () => {
await this.handleRouteSpecificFetch();
await this.revalidateCaches();
emitter.emit(BUS_EVENTS.WEBSOCKET_RECONNECT_COMPLETED);
};
}
@@ -253,54 +253,6 @@ describe('ReconnectService', () => {
});
});
describe('revalidateCaches', () => {
it('should dispatch revalidate actions for every cacheable model returned by the server', async () => {
storeMock.dispatch.mockResolvedValueOnce({
label: 'labelKey',
inbox: 'inboxKey',
team: 'teamKey',
canned_response: 'cannedKey',
account_user: 'accountUserKey',
});
await reconnectService.revalidateCaches();
expect(storeMock.dispatch).toHaveBeenCalledWith('accounts/getCacheKeys');
expect(storeMock.dispatch).toHaveBeenCalledWith('labels/revalidate', {
newKey: 'labelKey',
});
expect(storeMock.dispatch).toHaveBeenCalledWith('inboxes/revalidate', {
newKey: 'inboxKey',
});
expect(storeMock.dispatch).toHaveBeenCalledWith('teams/revalidate', {
newKey: 'teamKey',
});
expect(storeMock.dispatch).toHaveBeenCalledWith(
'revalidateCannedResponses',
{ newKey: 'cannedKey' }
);
expect(storeMock.dispatch).toHaveBeenCalledWith('agents/revalidate', {
newKey: 'accountUserKey',
});
});
it('should skip dispatches for models the server does not yet emit', async () => {
storeMock.dispatch.mockResolvedValueOnce({
label: 'labelKey',
inbox: 'inboxKey',
team: 'teamKey',
// canned_response / account_user omitted (older server)
});
await reconnectService.revalidateCaches();
expect(storeMock.dispatch).not.toHaveBeenCalledWith(
'revalidateCannedResponses',
expect.anything()
);
expect(storeMock.dispatch).not.toHaveBeenCalledWith(
'agents/revalidate',
expect.anything()
);
});
});
describe('handleRouteSpecificFetch', () => {
it('should fetch conversations and messages if current route is a conversation route', async () => {
isAConversationRoute.mockReturnValue(true);
@@ -362,12 +314,10 @@ describe('ReconnectService', () => {
});
describe('onReconnect', () => {
it('should handle route-specific fetch, revalidate caches, and emit WEBSOCKET_RECONNECT_COMPLETED event', async () => {
it('should handle route-specific fetch and emit WEBSOCKET_RECONNECT_COMPLETED event', async () => {
reconnectService.handleRouteSpecificFetch = vi.fn();
reconnectService.revalidateCaches = vi.fn();
await reconnectService.onReconnect();
expect(reconnectService.handleRouteSpecificFetch).toHaveBeenCalled();
expect(reconnectService.revalidateCaches).toHaveBeenCalled();
expect(emitter.emit).toHaveBeenCalledWith(
BUS_EVENTS.WEBSOCKET_RECONNECT_COMPLETED
);
@@ -163,10 +163,6 @@ export const actions = {
commit(types.default.SET_ACCOUNT_UI_FLAG, { isFetchingLimits: false });
}
},
getCacheKeys: async () => {
return AccountAPI.getCacheKeys();
},
};
export const mutations = {
@@ -14,9 +14,6 @@ class BaseActionCableConnector {
) {
const websocketURL = websocketHost ? `${websocketHost}/cable` : undefined;
this.connected = new Promise(resolve => {
this.resolveConnected = resolve;
});
this.consumer = createConsumer(websocketURL);
this.subscription = this.consumer.subscriptions.create(
{
@@ -29,10 +26,6 @@ class BaseActionCableConnector {
updatePresence() {
this.perform('update_presence');
},
connected: () => {
this.resolveConnected();
this.onConnected();
},
received: this.onReceived,
disconnected: () => {
BaseActionCableConnector.isDisconnected = true;
@@ -81,9 +74,6 @@ class BaseActionCableConnector {
}, RECONNECT_INTERVAL);
};
// eslint-disable-next-line class-methods-use-this
onConnected = () => {};
// eslint-disable-next-line class-methods-use-this
onReconnect = () => {};