fix: concurrent stale race, Redis expire leak, missing invalidation, UI Suspense

- Fix integration data cache: set pending synchronously before creating
  fetch to prevent concurrent stale-revalidation from spawning duplicate
  upstream calls
- Redis cache handler: skip persisting entries with expire=0 (dynamic
  entries should not be stored in Redis)
- Add missing invalidation: user.changePassword, group.updateGroup,
  group.transferOwnership
- Move SearchEngineOptimization inside Suspense in root layout
- Add Suspense fallback loader to root layout
- Move searchParams resolution inside Suspense in login page
This commit is contained in:
Thomas Camlong committed 2026-08-07 20:53:36 +02:00
1 parent 38ac8f3075
commit fbb2eddd87
6 files changed
+30 -21

No files matched your search

+7 -6
View File
@@ -39,6 +39,11 @@ export const getCachedIntegrationData = async <T>(
}
}
// Set pending synchronously BEFORE creating the fetch promise to prevent
// concurrent stale-revalidation from spawning duplicate upstream calls.
const placeholder = existing ?? { data: undefined as T, timestamp: 0 };
if (!existing) cache.set(key, placeholder);
const pending = fetcher()
.then((data) => {
cache.set(key, { data, timestamp: Date.now() });
@@ -67,13 +72,9 @@ export const getCachedIntegrationData = async <T>(
throw error;
});
if (existing) {
existing.pending = pending;
return existing.data;
}
placeholder.pending = pending;
cache.set(key, { data: undefined as T, timestamp: 0, pending });
return pending;
return existing ? existing.data : pending;
};
export const invalidateIntegrationDataCache = (integrationId: string) => {