gendesign/tradein-mvp/frontend/src/app/providers.tsx
Light1YT 6444dc0ebd
Some checks failed
CI / backend-tests (push) Has been skipped
CI / frontend-tests (push) Has been skipped
CI / changes (pull_request) Successful in 6s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / changes (push) Successful in 7s
Deploy Trade-In / build-backend (push) Blocked by required conditions
Deploy Trade-In / build-frontend (push) Blocked by required conditions
Deploy Trade-In / build-browser (push) Blocked by required conditions
Deploy Trade-In / test (push) Blocked by required conditions
Deploy Trade-In / deploy (push) Blocked by required conditions
Deploy Trade-In / changes (push) Has been cancelled
fix(tradein-frontend): остановить retry-storm на 401 — refetchOnMount:false + backoff (#800)
На стойком 401/протухшей сессии фронт повторял /me + /trade-in/quota в
бесконечном цикле (184+ запросов): errored query + дефолтный
refetchOnMount=true → каждый новый observer (useMe дёргают RouteGuard,
Topbar, UserMenu, useBrand) ре-фетчил при mount = re-subscribe storm.
Глобально выключаем refetchOnMount/onReconnect в QueryClient + экспоненциальный
retryDelay (cap 30s); те же гарды на useQuota. retry-предикат 4xx→false уже был.
Graceful session-screen (RouteGuard/NoAccessScreen) уже на месте.

Closes #800
2026-06-13 16:47:58 +05:00

40 lines
2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { useState } from "react";
export function Providers({ children }: { children: React.ReactNode }) {
const [client] = useState(
() =>
new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60_000,
refetchOnWindowFocus: false,
// #800: errored query + дефолтный refetchOnMount=true → каждый новый
// observer (Topbar/UserMenu/useBrand все дёргают useMe) ре-фетчит при
// mount → re-subscribe storm на стойком 401. Выключаем глобально: данные
// и так staleTime-кэшируются, а auth/quota не должны бить бэк на ремаунт.
refetchOnMount: false,
refetchOnReconnect: false,
// #770 finding #26 + #800: не ретраить 4xx (401/403/429) — иначе на
// rate-limit/протухшей сессии дефолтные 3 ретрая превращаются в шторм
// в собственный бэкенд. Ретраим только 5xx/сеть, ≤2 раза.
retry: (failureCount, error) => {
const status = (error as { status?: number } | null)?.status;
if (typeof status === "number" && status >= 400 && status < 500) return false;
return failureCount < 2;
},
// #800: экспоненциальный backoff на сетевых/5xx ретраях (1s, 2s, 4s…
// capped 30s) — никаких немедленных повторов, мягко к бэкенду.
retryDelay: (attemptIndex) =>
Math.min(1000 * 2 ** attemptIndex, 30_000),
},
mutations: {
retry: false,
},
},
}),
);
return <QueryClientProvider client={client}>{children}</QueryClientProvider>;
}