Skip to content

Feature/query caching and debug flags #961

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to

## Added

- 🚀(frontend) Adds api caching #961
- ✨(back) allow theme customnization using a configuration file #948
- ✨ Add a custom callout block to the editor #892
- 🚩(frontend) version MIT only #911
Expand Down
23 changes: 22 additions & 1 deletion src/backend/impress/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,14 @@ class Base(Configuration):
* DB_USER
"""

DEBUG = False
DEBUG_RAW = values.Value(
default=False,
environ_name="DEBUG",
environ_prefix=None,
)
DEBUG_NAMESPACES = DEBUG_RAW.split(",") if isinstance(DEBUG_RAW, str) else []
DEBUG = bool(DEBUG_RAW)
Comment on lines +61 to +67
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not really confident with this and It's I think error prone.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, it is.

Arguably we can remove the extended DEBUG handling for the backend.

I see too options:
A: Remove the debug handling for the backend
B: Write a dedicated function to handle it solidly

Which to choose?


USE_SWAGGER = False

API_VERSION = "v1.0"
Expand Down Expand Up @@ -761,6 +768,9 @@ def post_setup(cls):
"OIDC_ALLOW_DUPLICATE_EMAILS cannot be set to True simultaneously. "
)

if cls.DEBUG and "no-cache" in cls.DEBUG_NAMESPACES:
cls.THEME_CUSTOMIZATION_CACHE_TIMEOUT = 0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can directly set this settings in your environment variable IMO

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes in env.d\development\common.

Copy link
Collaborator Author

@rvveber rvveber May 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as it is only theme customization we are caching.
But what if there will be more in the future?

In an attempt to achieve the same functionality on the backend
i found this to be a functional example to illustrate how the debug namespaces would work.
(The frontend has the same functionality)



class Build(Base):
"""Settings used when the application is built.
Expand Down Expand Up @@ -795,6 +805,17 @@ class Development(Base):
CSRF_TRUSTED_ORIGINS = ["http://localhost:8072", "http://localhost:3000"]
DEBUG = True

# Enable debug namespaces as needed
#
# DEBUG_NAMESPACES = [
# 'no-cache',
# 'features:language'
# ]
#
# They can be enabled in all environments
# via DEBUG environment variable
# DEBUG="features:language,no-cache,..."

SESSION_COOKIE_NAME = "impress_sessionid"

USE_SWAGGER = True
Expand Down
4 changes: 3 additions & 1 deletion src/frontend/apps/impress/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@
"@openfun/cunningham-react": "3.0.0",
"@react-pdf/renderer": "4.3.0",
"@sentry/nextjs": "9.15.0",
"@tanstack/react-query": "5.75.4",
"@tanstack/query-sync-storage-persister": "5.76.0",
"@tanstack/react-query": "5.75.5",
"@tanstack/react-query-persist-client": "5.76.0",
"canvg": "4.0.3",
"clsx": "2.1.1",
"cmdk": "1.1.1",
Expand Down
37 changes: 29 additions & 8 deletions src/frontend/apps/impress/src/core/AppProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { CunninghamProvider } from '@openfun/cunningham-react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister';
import { QueryClient } from '@tanstack/react-query';
import type { Persister } from '@tanstack/react-query-persist-client';
import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client';
import debug from 'debug';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { useEffect, useMemo } from 'react';

import { useCunninghamTheme } from '@/cunningham';
import { Auth, KEY_AUTH, setAuthUrl } from '@/features/auth';
Expand All @@ -13,12 +17,16 @@ import { ConfigProvider } from './config/';
* QueryClient:
* - defaultOptions:
* - staleTime:
* - global cache duration - we decided 3 minutes
* - It can be overridden to each query
* - global time until cache is considered stale and will be refetched in the background
* - instant if debug flag "no-cache" active - 3 minutes otherwise
* - gcTime:
* - global time until cache is purged from the persister and needs to be renewed
* - since its cached in localStorage, we can set it to a long time (48h)
*/
const defaultOptions = {
queries: {
staleTime: 1000 * 60 * 3,
staleTime: debug.enabled('no-cache') ? 0 : 1000 * 60 * 3, // 3 minutes
gcTime: debug.enabled('no-cache') ? 0 : 1000 * 60 * 60 * 48, // 48 hours
Comment on lines +28 to +29
Copy link
Collaborator

@AntoLC AntoLC May 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About the debug, I am not sure it is the best way to do.
As a developer I like to be near the production setting, it helps me in this case to see which query I have to invalidate. If I have no-cache, I will not know that when I change the title of a document I need to invalidate the cache of the document list request.

After if you don't want cache, we could set these variables from settings:

Suggested change
staleTime: debug.enabled('no-cache') ? 0 : 1000 * 60 * 3, // 3 minutes
gcTime: debug.enabled('no-cache') ? 0 : 1000 * 60 * 60 * 48, // 48 hours
staleTime: process.env.NEXT_PUBLIC_CACHE_STALE_TIME,
gcTime: process.env.NEXT_PUBLIC_CACHE_GC_TIME

By default they will be in .env:

NEXT_PUBLIC_CACHE_STALE_TIME=1000 * 60 * 3
NEXT_PUBLIC_CACHE_GC_TIME=1000 * 60 * 60 * 48

but in .env.local you could set them as you want, .env.local is not versioned:

NEXT_PUBLIC_CACHE_STALE_TIME=0
NEXT_PUBLIC_CACHE_GC_TIME=0

https://nextjs.org/docs/pages/guides/environment-variables#environment-variable-load-order

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback.

  1. Debug Mode and Cache: The debug flag for cache control is off by default, aligning with production behavior. Developers who enable it for local debugging are intentionally altering cache settings for temporary diagnostic purposes.

  2. Data Updates and Cache Invalidation: For data modifications (e.g., updating a document title), we should adhere to the mutation pattern (as demonstrated here). This ensures that related queries are correctly invalidated and fresh data is fetched.

  3. Configurability of staleTime and gcTime:

    • Making the default staleTime and gcTime configurable via environment variables could introduce complexity. Because each useQuery can override defaults - we would need to add env configuration for each useQuery that has a different staleTime and gcTime.
    • Generally, we, as developers designing the queries, are best positioned to determine appropriate staleTime and gcTime values based on the nature of the data. Allowing arbitrary instance-level customization or overly flexible environment configurations might lead to inconsistent caching behavior and unexpected issues. It's preferable to establish sensible, well-reasoned defaults within the codebase for different types of queries.

retry: 1,
},
};
Expand All @@ -29,11 +37,21 @@ const queryClient = new QueryClient({
export function AppProvider({ children }: { children: React.ReactNode }) {
const { theme } = useCunninghamTheme();
const { replace } = useRouter();

const initializeResizeListener = useResponsiveStore(
(state) => state.initializeResizeListener,
);

const persister = useMemo(() => {
// Create persister only when the browser is available
if (typeof window !== 'undefined') {
return createSyncStoragePersister({
storage: window.localStorage,
});
}
// Return undefined otherwise (PersistQueryClientProvider handles undefined persister gracefully)
return undefined;
}, []);

useEffect(() => {
return initializeResizeListener();
}, [initializeResizeListener]);
Expand All @@ -60,12 +78,15 @@ export function AppProvider({ children }: { children: React.ReactNode }) {
}, [replace]);

return (
<QueryClientProvider client={queryClient}>
<PersistQueryClientProvider
client={queryClient}
persistOptions={{ persister: persister as Persister }}
>
<CunninghamProvider theme={theme}>
<ConfigProvider>
<Auth>{children}</Auth>
</ConfigProvider>
</CunninghamProvider>
</QueryClientProvider>
</PersistQueryClientProvider>
);
}
30 changes: 5 additions & 25 deletions src/frontend/apps/impress/src/core/config/api/useConfig.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useQuery } from '@tanstack/react-query';
import debug from 'debug';

import { APIError, errorCauses, fetchAPI } from '@/api';
import { Theme } from '@/cunningham/';
Expand All @@ -19,45 +20,24 @@ interface ConfigResponse {
SENTRY_DSN?: string;
}

const LOCAL_STORAGE_KEY = 'docs_config';

function getCachedTranslation() {
try {
const jsonString = localStorage.getItem(LOCAL_STORAGE_KEY);
return jsonString ? (JSON.parse(jsonString) as ConfigResponse) : undefined;
} catch {
return undefined;
}
}

function setCachedTranslation(translations: ConfigResponse) {
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(translations));
}

export const getConfig = async (): Promise<ConfigResponse> => {
const response = await fetchAPI(`config/`);

if (!response.ok) {
throw new APIError('Failed to get the doc', await errorCauses(response));
throw new APIError('Failed to get the config', await errorCauses(response));
}

const config = response.json() as Promise<ConfigResponse>;
setCachedTranslation(await config);

return config;
};

export const KEY_CONFIG = 'config';
export const QKEY_CONFIG = 'config';

export function useConfig() {
const cachedData = getCachedTranslation();
const oneHour = 1000 * 60 * 60;

return useQuery<ConfigResponse, APIError, ConfigResponse>({
queryKey: [KEY_CONFIG],
queryKey: [QKEY_CONFIG],
queryFn: () => getConfig(),
initialData: cachedData,
staleTime: oneHour,
initialDataUpdatedAt: Date.now() - oneHour, // Force initial data to be considered stale
staleTime: debug.enabled('no-cache') ? 0 : 1000 * 60 * 60, // 1 hour
});
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { UseQueryOptions, useQuery } from '@tanstack/react-query';
import debug from 'debug';

import { APIError, errorCauses, fetchAPI } from '@/api';

Expand Down Expand Up @@ -33,7 +34,7 @@ export function useAuthQuery(
return useQuery<User, APIError, User>({
queryKey: [KEY_AUTH],
queryFn: getMe,
staleTime: 1000 * 60 * 15, // 15 minutes
staleTime: debug.enabled('no-cache') ? 0 : 1000 * 60 * 15, // 15 minutes
...queryConfig,
});
}
22 changes: 22 additions & 0 deletions src/frontend/apps/impress/src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,28 @@ export default function App({ Component, pageProps }: AppPropsWithLayout) {
const getLayout = Component.getLayout ?? ((page) => page);
const { t } = useTranslation();

if (process.env.NODE_ENV === 'development') {
/**
* Enable debug namespaces as needed
*
* They can be enabled:
*
* via DEBUG environment variable
* DEBUG="features:language,no-cache,..."
*
* via browser console
* window.debug = "features:language,no-cache,...";
*
* via Local storage
* window.localStorage.debug = "features:language,no-cache,...";
*
* via code (uses Local storage)
* import debug from 'debug';
* debug.enable('no-cache,features:language,...');
*/
//debug.enable('no-cache,features:language');
}

Comment on lines +21 to +42
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you use this part ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is just here to illustrate usage for future development and enable quick toggle from code.

return (
<>
<Head>
Expand Down
45 changes: 36 additions & 9 deletions src/frontend/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4881,29 +4881,56 @@
dependencies:
"@typescript-eslint/utils" "^8.18.1"

"@tanstack/[email protected]":
version "5.75.4"
resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.75.4.tgz#e05f2fe4145fb5354271ad19e63eec61f6ce3012"
integrity sha512-pcqOUgWG9oGlzkfRQQMMsEFmtQu0wq81A414CtELZGq+ztVwSTAaoB3AZRAXQJs88LmNMk2YpUKuQbrvzNDyRg==
"@tanstack/[email protected]":
version "5.75.5"
resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.75.5.tgz#198c27c21b5d15aad09167af8a021f214e194d4b"
integrity sha512-kPDOxtoMn2Ycycb76Givx2fi+2pzo98F9ifHL/NFiahEDpDwSVW6o12PRuQ0lQnBOunhRG5etatAhQij91M3MQ==

"@tanstack/[email protected]":
version "5.76.0"
resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.76.0.tgz#3b4d5d34ce307ba0cf7d1a3e90d7adcdc6c46be0"
integrity sha512-FN375hb8ctzfNAlex5gHI6+WDXTNpe0nbxp/d2YJtnP+IBM6OUm7zcaoCW6T63BawGOYZBbKC0iPvr41TteNVg==

"@tanstack/[email protected]":
version "5.74.7"
resolved "https://registry.yarnpkg.com/@tanstack/query-devtools/-/query-devtools-5.74.7.tgz#c9b022b386ac86e6395228b5d6912e6444b3b971"
integrity sha512-nSNlfuGdnHf4yB0S+BoNYOE1o3oAH093weAYZolIHfS2stulyA/gWfSk/9H4ZFk5mAAHb5vNqAeJOmbdcGPEQw==

"@tanstack/[email protected]":
version "5.76.0"
resolved "https://registry.yarnpkg.com/@tanstack/query-persist-client-core/-/query-persist-client-core-5.76.0.tgz#a3bcdd687384dc6b5b61b402bef153ad54515321"
integrity sha512-xcTZjILf4q49Nsl6wcnhBYZ4O0gpnuNwV6vPIEWIrwTuSNWz2zd/g9bc8SxnXy7xCV8SM1H0IJn8KjLQIUb2ag==
dependencies:
"@tanstack/query-core" "5.76.0"

"@tanstack/[email protected]":
version "5.76.0"
resolved "https://registry.yarnpkg.com/@tanstack/query-sync-storage-persister/-/query-sync-storage-persister-5.76.0.tgz#29643062f1a424873afb22032ce70ee72436bb9b"
integrity sha512-N8d8voY61XkM+jfXTySduLrevD6wRM3pwQ1kG0syLiWWx/sX2+CpaTMSPr0GggjQuhmjhUPo83LaV+e449tizA==
dependencies:
"@tanstack/query-core" "5.76.0"
"@tanstack/query-persist-client-core" "5.76.0"

"@tanstack/[email protected]":
version "5.75.4"
resolved "https://registry.yarnpkg.com/@tanstack/react-query-devtools/-/react-query-devtools-5.75.4.tgz#89614363d63c997ade81ade4a18e90b57512d4d8"
integrity sha512-CSJZWa316EFtLZtr6RQLAnqWb1MESzyZ7j0bMQjuhYas5FDp/3MA7G6RE4hWauqCCDsNIfIm2Rnm1zJTZVye/w==
dependencies:
"@tanstack/query-devtools" "5.74.7"

"@tanstack/[email protected]":
version "5.75.4"
resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.75.4.tgz#721e1cdf7debb110671f558dc2b6276f637554a5"
integrity sha512-Vf65pzYRkf8fk9SP1ncIZjvaXszBhtsvpf+h45Y/9kOywOrVZfBGUpCdffdsVzbmBzmz6TCFes9bM0d3pRrIsA==
"@tanstack/[email protected]":
version "5.76.0"
resolved "https://registry.yarnpkg.com/@tanstack/react-query-persist-client/-/react-query-persist-client-5.76.0.tgz#97718fec844708cb98a5902d4b1eeb72adea555b"
integrity sha512-QPKgkHX1yC1Ec21FTQHBTbQcHYI+6157DgsmxABp94H7/ZUJ3szZ7wdpdBPQyZ9VxBXlKRN+aNZkOPC90+r/uA==
dependencies:
"@tanstack/query-persist-client-core" "5.76.0"

"@tanstack/[email protected]":
version "5.75.5"
resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.75.5.tgz#650a6b58c7bcd4c33062426e05c4467571148a4f"
integrity sha512-QrLCJe40BgBVlWdAdf2ZEVJ0cISOuEy/HKupId1aTKU6gPJZVhSvZpH+Si7csRflCJphzlQ77Yx6gUxGW9o0XQ==
dependencies:
"@tanstack/query-core" "5.75.4"
"@tanstack/query-core" "5.75.5"

"@tanstack/[email protected]":
version "8.20.6"
Expand Down
Loading