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
На стойком 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
40 lines
2 KiB
TypeScript
40 lines
2 KiB
TypeScript
"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>;
|
||
}
|