diff --git a/docs/framework/preact/guides/ssr.md b/docs/framework/preact/guides/ssr.md index 329d0a312be..babb2423539 100644 --- a/docs/framework/preact/guides/ssr.md +++ b/docs/framework/preact/guides/ssr.md @@ -489,12 +489,37 @@ This refetching of stale queries is a perfect match when caching markup in a CDN ### High memory consumption on server -In case you are creating the `QueryClient` for every request, Preact Query creates the isolated cache for this client, which is preserved in memory for the `gcTime` period. That may lead to high memory consumption on server in case of high number of requests during that period. +On the server, `gcTime` defaults to `Infinity`. With `Infinity`, no garbage collection timer is scheduled, and the cache is freed from memory together with the `QueryClient` once the request finishes. -On the server, `gcTime` defaults to `Infinity` which disables manual garbage collection and will automatically clear memory once a request has finished. If you are explicitly setting a non-Infinity `gcTime` then you will be responsible for clearing the cache early. +Any finite `gcTime` schedules a timer for each query, and that timer keeps the query in memory until it fires. In Node.js, a timer also keeps alive the [`AsyncLocalStorage`](https://nodejs.org/api/async_context.html#class-asynclocalstorage) context it was created in. If your server framework stores the request in that context, the request stays in memory even after the response is sent, until the timer fires. -Avoid setting `gcTime` to `0` as it may result in a hydration error. This occurs because the [Hydration Boundary](../reference/functions/HydrationBoundary.md) places necessary data into the cache for rendering, but if the garbage collector removes the data before the rendering completes, issues may arise. If you require a shorter `gcTime`, we recommend setting it to `2 * 1000` to allow sufficient time for the app to reference the data. +This is easy to miss: a `gcTime` set in `defaultOptions` applies on the server too, so a value intended for the client also replaces `Infinity` on the server. -To clear the cache after it is not needed and to lower memory consumption, you can add a call to [`queryClient.clear()`](../reference/classes/QueryClient.md#clear) after the request is handled and dehydrated state has been sent to the client. +```tsx +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + // This also applies on the server + gcTime: 1000 * 60, + }, + }, +}) +``` + +If you only want a custom `gcTime` in the browser, keep `Infinity` on the server: + +```tsx +import { environmentManager, QueryClient } from '@tanstack/preact-query' + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + gcTime: environmentManager.isServer() ? Infinity : 1000 * 60, + }, + }, +}) +``` + +Avoid setting `gcTime` to `0` as it may result in a hydration error. This occurs because the [Hydration Boundary](../reference/functions/HydrationBoundary.md) places necessary data into the cache for rendering, but if the garbage collector removes the data before the rendering completes, issues may arise. Short finite values have the same risk: a prefetched query that finishes early can be removed once its `gcTime` passes, while a slower query is still being prefetched and before `dehydrate` runs. Keep `Infinity` on the server where you can. -Alternatively, you can set a smaller `gcTime`. +If you set a finite `gcTime` on the server, call [`queryClient.clear()`](../reference/classes/QueryClient.md#clear) once you no longer need the client and all of its queries have settled, for example after you have dehydrated or rendered with it. This removes every query from the cache and cancels its pending timer. A fetch that is still in flight when you call `clear()` schedules a new timer once it settles, so wait for pending fetches to settle first. diff --git a/docs/framework/react/guides/ssr.md b/docs/framework/react/guides/ssr.md index 5b64f79eedf..027c8b24c3a 100644 --- a/docs/framework/react/guides/ssr.md +++ b/docs/framework/react/guides/ssr.md @@ -565,15 +565,40 @@ This refetching of stale queries is a perfect match when caching markup in a CDN ### High memory consumption on server -In case you are creating the `QueryClient` for every request, React Query creates the isolated cache for this client, which is preserved in memory for the `gcTime` period. That may lead to high memory consumption on server in case of high number of requests during that period. +On the server, `gcTime` defaults to `Infinity`. With `Infinity`, no garbage collection timer is scheduled, and the cache is freed from memory together with the `QueryClient` once the request finishes. -On the server, `gcTime` defaults to `Infinity` which disables manual garbage collection and will automatically clear memory once a request has finished. If you are explicitly setting a non-Infinity `gcTime` then you will be responsible for clearing the cache early. +Any finite `gcTime` schedules a timer for each query, and that timer keeps the query in memory until it fires. In Node.js, a timer also keeps alive the [`AsyncLocalStorage`](https://nodejs.org/api/async_context.html#class-asynclocalstorage) context it was created in, and frameworks like Next.js store the whole request in that context. So with a finite `gcTime`, the request stays in memory even after the response is sent, until the timer fires. -Avoid setting `gcTime` to `0` as it may result in a hydration error. This occurs because the [Hydration Boundary](../reference/functions/HydrationBoundary.md) places necessary data into the cache for rendering, but if the garbage collector removes the data before the rendering completes, issues may arise. If you require a shorter `gcTime`, we recommend setting it to `2 * 1000` to allow sufficient time for the app to reference the data. +This is easy to miss: a `gcTime` set in `defaultOptions` applies on the server too, so a value intended for the client also replaces `Infinity` on the server. -To clear the cache after it is not needed and to lower memory consumption, you can add a call to [`queryClient.clear()`](../reference/classes/QueryClient.md#clear) after the request is handled and dehydrated state has been sent to the client. +```tsx +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + // This also applies on the server + gcTime: 1000 * 60, + }, + }, +}) +``` + +If you only want a custom `gcTime` in the browser, keep `Infinity` on the server: + +```tsx +import { environmentManager, QueryClient } from '@tanstack/react-query' + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + gcTime: environmentManager.isServer() ? Infinity : 1000 * 60, + }, + }, +}) +``` + +Avoid setting `gcTime` to `0` as it may result in a hydration error. This occurs because the [Hydration Boundary](../reference/functions/HydrationBoundary.md) places necessary data into the cache for rendering, but if the garbage collector removes the data before the rendering completes, issues may arise. Short finite values have the same risk: a prefetched query that finishes early can be removed once its `gcTime` passes, while a slower query is still being prefetched and before `dehydrate` runs. Keep `Infinity` on the server where you can. -Alternatively, you can set a smaller `gcTime`. +If you set a finite `gcTime` on the server, call [`queryClient.clear()`](../reference/classes/QueryClient.md#clear) once you no longer need the client and all of its queries have settled, for example after you have dehydrated or rendered with it. This removes every query from the cache and cancels its pending timer. A fetch that is still in flight when you call `clear()` schedules a new timer once it settles, so wait for pending fetches to settle first. ### Caveat for Next.js rewrites diff --git a/docs/framework/solid/guides/ssr.md b/docs/framework/solid/guides/ssr.md index fdced1849f3..302c7275614 100644 --- a/docs/framework/solid/guides/ssr.md +++ b/docs/framework/solid/guides/ssr.md @@ -194,4 +194,35 @@ A query's staleness is based on `dataUpdatedAt`, which is set to when it resolve ### High memory consumption on server -Because the `QueryClient` is created fresh for every request, its cache is held in memory for the duration of that request. Under high request volume this can add up. `gcTime` defaults to `Infinity` on the server, so unless you configure it lower, memory for a request's cache isn't reclaimed automatically — in most cases this is fine, since the whole `QueryClient` instance becomes eligible for garbage collection once the request finishes and nothing references it anymore. +On the server, `gcTime` defaults to `Infinity`. With `Infinity`, no garbage collection timer is scheduled, and the cache is freed from memory together with the `QueryClient` once the request finishes. + +Any finite `gcTime` schedules a timer for each query, and that timer keeps the query in memory until it fires. In Node.js, a timer also keeps alive the [`AsyncLocalStorage`](https://nodejs.org/api/async_context.html#class-asynclocalstorage) context it was created in. If your server framework stores the request in that context, the request stays in memory even after the response is sent, until the timer fires. + +This is easy to miss: a `gcTime` set in `defaultOptions` applies on the server too, so a value intended for the client also replaces `Infinity` on the server. + +```tsx +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + // This also applies on the server + gcTime: 1000 * 60, + }, + }, +}) +``` + +If you only want a custom `gcTime` in the browser, keep `Infinity` on the server: + +```tsx +import { environmentManager, QueryClient } from '@tanstack/solid-query' + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + gcTime: environmentManager.isServer() ? Infinity : 1000 * 60, + }, + }, +}) +``` + +If you set a finite `gcTime` on the server, call [`queryClient.clear()`](../reference/classes/QueryClient.md#clear) once the response has been sent and all of the client's queries have settled. This removes every query from the cache and cancels its pending timer. A fetch that is still in flight when you call `clear()` schedules a new timer once it settles, so wait for pending fetches to settle first. diff --git a/docs/framework/vue/guides/ssr.md b/docs/framework/vue/guides/ssr.md index 716c2a6353e..c3826099192 100644 --- a/docs/framework/vue/guides/ssr.md +++ b/docs/framework/vue/guides/ssr.md @@ -282,11 +282,38 @@ onServerPrefetch(async () => { ### High memory consumption on server -In case you are creating the `QueryClient` for every request, Vue Query creates the isolated cache for this client, which is preserved in memory for the `gcTime` period. That may lead to high memory consumption on server in case of high number of requests during that period. +On the server, `gcTime` defaults to `Infinity`. With `Infinity`, no garbage collection timer is scheduled, and the cache is freed from memory together with the `QueryClient` once the request finishes. -On the server, `gcTime` defaults to `Infinity` which disables manual garbage collection and will automatically clear memory once a request has finished. If you are explicitly setting a non-Infinity `gcTime` then you will be responsible for clearing the cache early. +Any finite `gcTime` schedules a timer for each query, and that timer keeps the query in memory until it fires. In Node.js, a timer also keeps alive the [`AsyncLocalStorage`](https://nodejs.org/api/async_context.html#class-asynclocalstorage) context it was created in. If your server framework stores the request in that context, the request stays in memory even after the response is sent, until the timer fires. -To clear the cache after it is not needed and to lower memory consumption, you can add a call to [`queryClient.clear()`](../reference/classes/QueryClient.md#clear) after the request is handled and dehydrated state has been sent to the client. +This is easy to miss: a `gcTime` set in `defaultOptions` applies on the server too, so a value intended for the client also replaces `Infinity` on the server. + +```ts +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + // This also applies on the server + gcTime: 1000 * 60, + }, + }, +}) +``` + +If you only want a custom `gcTime` in the browser, keep `Infinity` on the server: + +```ts +import { environmentManager, QueryClient } from '@tanstack/vue-query' + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + gcTime: environmentManager.isServer() ? Infinity : 1000 * 60, + }, + }, +}) +``` + +If you set a finite `gcTime` on the server, call [`queryClient.clear()`](../reference/classes/QueryClient.md#clear) once you no longer need the client and all of its queries have settled, for example after you have dehydrated or rendered with it. This removes every query from the cache and cancels its pending timer. A fetch that is still in flight when you call `clear()` schedules a new timer once it settles, so wait for pending fetches to settle first. ## `dehydrate`/`hydrate` options