TanStack Query предоставляет внутренний кеш как реактивную структуру
данных, где каждое изменение состояния запроса, мутации или подписки
отражается через события кеша. Мониторинг кеша в этой системе опирается
на наблюдение за QueryCache, MutationCache и
QueryClient, позволяя фиксировать жизненный цикл данных,
диагностировать повторные запросы, отслеживать инвалидирование и
анализировать поведение приложения в реальном времени.
Кеш в TanStack Query состоит из нескольких ключевых уровней:
QueryCache — хранит все query-объектыMutationCache — хранит состояние мутацийQueryClient — координирует доступ ко всем кешам и
глобальным операциямМониторинг кеша всегда начинается с понимания того, что любое
изменение query (создание, обновление, инвалидирование) проходит через
QueryCache, а любые мутации — через
MutationCache.
Основной механизм наблюдения за изменениями кеша — подписка на
QueryCache.
import { QueryCache } from '@tanstack/query-core';
const queryCache = new QueryCache({
onError: (error, query) => {
console.log('Query error:', query.queryKey, error);
},
onSuccess: (data, query) => {
console.log('Query success:', query.queryKey);
},
});
Однако более низкоуровневый способ мониторинга — использование метода
subscribe, который позволяет отслеживать все события
изменения состояния query.
const unsubscribe = queryCache.subscribe((event) => {
console.log('Cache event:', event.type);
if (event.type === 'upd ated') {
console.log('Updated query:', event.query.queryKey);
}
if (event.type === 'removed') {
console.log('Removed query:', event.query.queryKey);
}
});
События могут включать:
added — добавление нового запросаupdated — обновление состояния запросаremoved — удаление из кешаПодписка на кеш позволяет строить внешние системы мониторинга, такие как логирование запросов или отправка метрик в аналитические сервисы.
В реальных приложениях чаще используется QueryClient,
который инкапсулирует кеши и предоставляет единый интерфейс доступа:
import { QueryClient } from '@tanstack/query-core';
const queryClient = new QueryClient();
const cache = queryClient.getQueryCache();
cache.subscribe((event) => {
console.log('Global cache event:', event);
});
Через QueryClient можно также отслеживать мутации:
const mutationCache = queryClient.getMutationCache();
mutationCache.subscribe((event) => {
console.log('Mutation event:', event.type);
});
Это важно для систем, где необходимо мониторить не только загрузку данных, но и изменения состояния на сервере.
Каждый query в кеше проходит через несколько состояний:
fetchingsuccesserrorstaleinactiveМониторинг этих переходов позволяет выявлять проблемы производительности и избыточные запросы.
cache.subscribe((event) => {
const query = event.query;
console.log({
key: query.queryKey,
state: query.state.status,
fetchStatus: query.state.fetchStatus,
updatedAt: query.state.dataUpdatedAt,
});
});
Особенно важны поля:
status — логическое состояние данныхfetchStatus — активность запросаdataUpdatedAt — время последнего обновленияОдним из ключевых аспектов мониторинга является отслеживание
invalidateQueries.
queryClient.invalidateQueries({
queryKey: ['users'],
});
С точки зрения кеша это вызывает цепочку событий:
staleМониторинг этих событий позволяет выявлять:
Логирование можно реализовать через глобальную подписку:
queryCache.subscribe((event) => {
if (event.type === 'updated' && event.query.state.isInvalidated) {
console.log('Invalidated query refetched:', event.query.queryKey);
}
});
Одним из практических сценариев мониторинга является подсчет активных запросов:
function getActiveQueries(cache) {
return cache.getAll().filter(q => q.state.fetchStatus === 'fetching');
}
Это позволяет:
Также можно отслеживать долгоживущие запросы:
setInterval(() => {
const active = getActiveQueries(queryClient.getQueryCache());
active.forEach(query => {
const duration = Date.now() - query.state.dataUpdatedAt;
if (duration > 10000) {
console.warn('Long running query:', query.queryKey);
}
});
}, 5000);
MutationCache отражает операции изменения данных на сервере. Его мониторинг критичен для контроля состояния side-effect операций.
mutationCache.subscribe((event) => {
switch (event.type) {
case 'updated':
console.log('Mutation updated:', event.mutation.state.status);
break;
case 'added':
console.log('Mutation started');
break;
case 'removed':
console.log('Mutation finished');
break;
}
});
Состояния мутаций:
idlependingsuccesserrorМониторинг позволяет выявлять:
TanStack Query Devtools использует те же механизмы подписки на кеш, предоставляя визуальное отображение состояния всех query и mutation.
Однако в production-среде чаще используется кастомное логирование:
queryCache.subscribe((event) => {
fetch('/logs', {
method: 'POST',
body: JSON.stringify({
type: event.type,
queryKey: event.query?.queryKey,
state: event.query?.state,
}),
});
});
Такой подход позволяет строить:
Сырые события кеша часто слишком детализированы, поэтому применяется агрегация:
const stats = {
queries: 0,
mutations: 0,
errors: 0,
};
queryCache.subscribe((event) => {
stats.queries++;
if (event.query?.state?.status === 'error') {
stats.errors++;
}
});
Мониторинг часто требует сегментации по queryKey:
function groupByKey(cache) {
return cache.getAll().reduce((acc, query) => {
const key = JSON.stringify(query.queryKey);
acc[key] = (acc[key] || 0) + 1;
return acc;
}, {});
}
Шумные запросы — это те, которые обновляются слишком часто:
const frequencyMap = new Map();
queryCache.subscribe((event) => {
const key = JSON.stringify(event.query.queryKey);
frequencyMap.se t(key, (frequencyMap.get(key) || 0) + 1);
});
Мониторинг кеша напрямую влияет на производительность. Основные ограничения:
Рекомендуется:
Пример оптимизации:
let timeout;
queryCache.subscribe((event) => {
clearTimeout(timeout);
timeout = setTimeout(() => {
console.log('Batch event processing');
}, 200);
});
При серверном рендеринге кеш может быть предварительно заполнен. Мониторинг гидратации позволяет выявлять несоответствия:
queryClient.hydrate(dehydratedState);
queryCache.subscribe((event) => {
if (event.type === 'added') {
console.log('Hydrated query:', event.query.queryKey);
}
});
Основные метрики:
При использовании persistence слоя кеш может восстанавливаться из localStorage или IndexedDB.
queryClient.getQueryCache().subscribe((event) => {
if (event.type === 'added') {
console.log('Restored from persistence:', event.query.queryKey);
}
});
Это позволяет отслеживать:
Полноценный мониторинг кеша обычно включает несколько слоев:
Такая архитектура позволяет превратить кеш TanStack Query в источник телеметрии, отражающий поведение всего слоя работы с серверными данными в приложении.