Batch операции

Batch операции в TanStack Query представляют собой механизм объединения нескольких запросов или мутаций в единые логические или транспортные группы для уменьшения количества сетевых обращений, оптимизации производительности и синхронизации состояния между клиентом и сервером. Концепция батчинга становится особенно важной в приложениях с высокой частотой обновлений данных, сложными графами зависимостей запросов и необходимостью минимизировать нагрузку на API.

В основе batch-операций лежит объединение нескольких логически независимых действий в одно физическое выполнение. В контексте TanStack Query это может касаться как запросов (queries), так и мутаций (mutations), а также внутренних обновлений кеша.

Ключевая цель:

  • сокращение количества HTTP-запросов
  • уменьшение задержек за счёт группировки операций
  • снижение нагрузки на сервер
  • консистентность состояния клиента

Батчинг не меняет семантику операций, он изменяет способ их исполнения.

Внутренний батчинг обновлений состояния

TanStack Query использует батчинг внутри себя для оптимизации реактивных обновлений. Когда несколько query или mutation завершаются почти одновременно, библиотека объединяет обновления состояния React, чтобы избежать множественных ререндеров.

Механизм работает на уровне scheduler:

  • результаты нескольких query обновляются в одном цикле
  • React получает один пакет изменений состояния
  • компоненты рендерятся один раз вместо нескольких

Это особенно заметно при параллельных запросах:

const query1 = useQuery({ queryKey: ['users'], queryFn: fetchUsers });
const query2 = useQuery({ queryKey: ['posts'], queryFn: fetchPosts });

Даже если оба запроса завершаются с небольшой разницей во времени, UI обновится единым блоком.

Батчинг в invalidation и refetch

Одним из наиболее важных сценариев батчинга является инвалидирование кеша.

При вызове:

queryClient.invalidateQueries(['users']);
queryClient.invalidateQueries(['posts']);

TanStack Query группирует эти операции, чтобы:

  • не запускать два отдельных цикла пересчёта кеша
  • не инициировать два независимых refetch-процесса
  • объединить подписки на обновления

В результате происходит единый проход по дереву query cache, где совпадающие ключи обрабатываются в одном цикле.

Батчинг мутаций и side-effects

Хотя TanStack Query не предоставляет классического batch API для мутаций, батчинг достигается через композицию mutation-логики и оптимистические обновления.

Типичный пример: массовое обновление сущностей.

const mutation = useMutation({
  mutationFn: async (updates) => {
    return fetch('/api/batch-update', {
      method: 'POST',
      body: JSON.stringify(updates),
    });
  },
});

Однако при отсутствии серверного batch-endpoint батчинг можно эмулировать:

Promise.all([
  updateUser({ id: 1, name: 'A' }),
  updateUser({ id: 2, name: 'B' }),
  updateUser({ id: 3, name: 'C' }),
]);

TanStack Query при этом:

  • аккумулирует invalidation
  • группирует обновления кеша
  • минимизирует количество rerender-циклов

Дедупликация как форма батчинга

Важной разновидностью batch-оптимизации является дедупликация запросов. TanStack Query автоматически объединяет идентичные запросы, если они:

  • имеют одинаковый queryKey
  • выполняются одновременно
  • не находятся в состоянии stale-refetch гонки

Пример:

useQuery({ queryKey: ['user', 1], queryFn: fetchUser });
useQuery({ queryKey: ['user', 1], queryFn: fetchUser });

Выполнится один HTTP-запрос, а результат будет разделён между подписчиками.

Это поведение фактически является батчингом на уровне транспортного слоя.

Батчинг через queryClient.setQueriesData

При массовом обновлении кеша TanStack Query позволяет изменять несколько query за один логический проход.

queryClient.setQueriesData(['cart'], (oldData) => {
  return oldData.map(item => ({
    ...item,
    price: item.price * 0.9,
  }));
});

Если аналогичная операция выполняется для нескольких ключей:

queryClient.setQueriesData(['cart'], updater);
queryClient.setQueriesData(['wishlist'], updater);
queryClient.setQueriesData(['orders'], updater);

внутренний scheduler объединяет обновления состояния, минимизируя количество ререндеров.

Батчинг в React-синхронизации

TanStack Query тесно связан с React batching model. В React 18 и выше используется автоматический batching, а TanStack Query интегрируется с ним следующим образом:

  • все setState внутри query обновляются в рамках одного event loop tick
  • async callbacks также могут быть батчены при использовании startTransition
  • подписки на query cache обновляются группами

Это особенно важно при массовых refetch:

queryClient.refetchQueries(['users']);
queryClient.refetchQueries(['posts']);
queryClient.refetchQueries(['comments']);

Даже при последовательных вызовах UI обновится один раз.

Батчинг в фоновом обновлении данных

При использовании stale-while-revalidate стратегии батчинг проявляется при фоновых refetch:

  • несколько устаревших query запускаются одновременно
  • запросы группируются по приоритетам
  • обновления кеша применяются пакетно

Пример:

useQuery({
  queryKey: ['dashboard'],
  queryFn: fetchDashboard,
  refetchInterval: 5000,
});

Если одновременно активны несколько таких query, библиотека стремится минимизировать сетевые пики и распределяет обновления по времени.

Батчинг через setQueryDefaults и глобальные настройки

Глобальная конфигурация позволяет косвенно управлять батчингом поведения:

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      refetchOnWindowFocus: true,
      staleTime: 10000,
    },
  },
});

При большом количестве активных query refetchOnWindowFocus может привести к всплеску запросов, однако внутренний batching сглаживает эффект, объединяя обновления кеша и ререндеры.

Серверный батчинг и TanStack Query

Наиболее эффективная форма batch-операций достигается при поддержке со стороны API. TanStack Query не навязывает формат, но хорошо работает с batch-endpoint моделями.

Пример серверного батча:

fetch('/api/batch', {
  method: 'POST',
  body: JSON.stringify({
    queries: [
      { endpoint: '/users' },
      { endpoint: '/posts' },
      { endpoint: '/comments' },
    ],
  }),
});

На клиенте:

const queryFn = async () => {
  const res = await fetch('/api/batch', {
    method: 'POST',
    body: JSON.stringify(payload),
  });

  return res.json();
};

TanStack Query в этом случае получает уже агрегированный результат и минимизирует количество cache operations.

Оптимизация invalidation цепочек

При сложных графах зависимостей кеша батчинг играет ключевую роль. Например:

  • изменение пользователя
  • инвалидирование профиля
  • инвалидирование постов
  • обновление статистики

Без батчинга это приводит к каскаду refetch-запросов.

TanStack Query объединяет такие операции в единый цикл:

  • сначала собираются все invalidate вызовы
  • затем строится список affected queries
  • выполняется единый execution pass

Это предотвращает экспоненциальный рост сетевой активности.

Практическое влияние батчинга на архитектуру

Батчинг влияет не только на производительность, но и на архитектуру данных:

  • стимулирует использование более крупных API-ответов
  • снижает необходимость дробных запросов
  • упрощает кеш-модель
  • уменьшает количество промежуточных состояний

При этом важно учитывать, что чрезмерный батчинг может привести к:

  • увеличению payload size
  • задержкам из-за ожидания агрегации
  • усложнению серверной логики

Батчинг и предсказуемость обновлений

Одним из побочных эффектов является изменение временной модели обновлений UI. Вместо последовательного потока обновлений появляется пакетное обновление состояния.

Это выражается в:

  • синхронном появлении нескольких UI-изменений
  • уменьшении промежуточных состояний загрузки
  • более стабильном render pipeline

TanStack Query стремится сохранить предсказуемость:

  • порядок операций сохраняется
  • консистентность кеша не нарушается
  • подписки обновляются атомарно

Глубокая интеграция батчинга с кеш-слоем

Query cache является центральным элементом батчинга. Все операции проходят через него:

  • fetch results
  • invalidation
  • optimistic updates
  • garbage collection

Батчинг обеспечивает, что:

  • изменения кеша не вызывают каскадных побочных эффектов
  • подписчики получают согласованные данные
  • обновления происходят в минимальном числе циклов

Эта модель делает TanStack Query эффективной при работе с большим количеством параллельных запросов и сложными UI-деревьями.