From 065b3de1db9f5d8dafd52adeb82e329283e9bbc4 Mon Sep 17 00:00:00 2001 From: Thomas Camlong Date: Mon, 22 Jun 2026 17:00:29 +0200 Subject: [PATCH] fix(beszel): leverage TanStack Query for stale-while-revalidate caching Server-side Redis cache reduced to 1s dedup window. TanStack Query (staleTime 10s, gcTime 48h, refetchInterval 10s) now controls freshness. SSR hydration from persister provides instant page loads with old data, background refetches hit Beszel directly every 10s. Fixes issue where widgets showed stale/errored data indefinitely due to long Redis cache durations preventing refetches. --- packages/request-handler/src/beszel.ts | 27 ++++++++----------- .../widgets/src/beszel-alerts/component.tsx | 5 ++-- .../src/beszel-system-grid/component.tsx | 2 +- .../src/beszel-system-stats/component.tsx | 7 ++--- .../src/beszel-system-table/component.tsx | 2 +- .../src/beszel/_shared/system-stats-modal.tsx | 2 +- 6 files changed, 21 insertions(+), 24 deletions(-) diff --git a/packages/request-handler/src/beszel.ts b/packages/request-handler/src/beszel.ts index b3828817d..c7e320e35 100644 --- a/packages/request-handler/src/beszel.ts +++ b/packages/request-handler/src/beszel.ts @@ -75,7 +75,7 @@ export const beszelSystemsRequestHandler = createCachedIntegrationRequestHandler }); return enriched; }, - cacheDuration: dayjs.duration(5, "seconds"), + cacheDuration: dayjs.duration(1, "second"), fallbackToStaleOnError: true, retry: { attempts: 2, delayMs: 500 }, queryKey: "beszelSystems", @@ -104,19 +104,19 @@ export const beszelAlertsRequestHandler = createCachedIntegrationRequestHandler< }); return { alerts, history }; }, - cacheDuration: dayjs.duration(15, "seconds"), + cacheDuration: dayjs.duration(1, "second"), fallbackToStaleOnError: true, retry: { attempts: 2, delayMs: 500 }, queryKey: "beszelAlerts", }); -const timePeriodConfig: Record = { - "1m": { type: "1m", perPage: 60, cacheSeconds: 5 }, - "1h": { type: "1m", perPage: 60, cacheSeconds: 60 }, - "12h": { type: "10m", perPage: 72, cacheSeconds: 300 }, - "24h": { type: "20m", perPage: 72, cacheSeconds: 600 }, - "1w": { type: "480m", perPage: 21, cacheSeconds: 1800 }, - "30d": { type: "480m", perPage: 90, cacheSeconds: 3600 }, +const timePeriodConfig: Record = { + "1m": { type: "1m", perPage: 60 }, + "1h": { type: "1m", perPage: 60 }, + "12h": { type: "10m", perPage: 72 }, + "24h": { type: "20m", perPage: 72 }, + "1w": { type: "480m", perPage: 21 }, + "30d": { type: "480m", perPage: 90 }, }; export interface BeszelStatsData { @@ -131,7 +131,7 @@ export const beszelStatsRequestHandler = createCachedIntegrationRequestHandler< >({ async requestAsync(integration, input) { const start = performance.now(); - const config = timePeriodConfig[input.timePeriod] ?? { type: "1m", perPage: 60, cacheSeconds: 60 }; + const config = timePeriodConfig[input.timePeriod] ?? { type: "1m", perPage: 60 }; const instance = await createIntegrationAsync(integration); const systemStats = await instance.getSystemStatsAsync(input.systemId, config.type, config.perPage); const containerStats = input.includeDocker @@ -147,13 +147,8 @@ export const beszelStatsRequestHandler = createCachedIntegrationRequestHandler< }); return { systemStats, containerStats }; }, - cacheDuration: dayjs.duration(60, "seconds"), +cacheDuration: dayjs.duration(1, "second"), fallbackToStaleOnError: true, retry: { attempts: 2, delayMs: 500 }, queryKey: "beszelStats", - cacheDurationForInput(input) { - const config = timePeriodConfig[input.timePeriod]; - if (!config) return undefined; - return dayjs.duration(config.cacheSeconds, "seconds"); - }, }); diff --git a/packages/widgets/src/beszel-alerts/component.tsx b/packages/widgets/src/beszel-alerts/component.tsx index faaa42771..7c2bcc06c 100644 --- a/packages/widgets/src/beszel-alerts/component.tsx +++ b/packages/widgets/src/beszel-alerts/component.tsx @@ -46,8 +46,9 @@ export default function BeszelAlertsWidget({ error: alertsError, isPending, } = clientApi.widget.beszel.getAlerts.useQuery(alertsInput, { - staleTime: 15_000, - refetchInterval: 15_000, + staleTime: 10_000, + gcTime: 48 * 60 * 60 * 1000, + refetchInterval: 10_000, retry: false, }); const utils = clientApi.useUtils(); diff --git a/packages/widgets/src/beszel-system-grid/component.tsx b/packages/widgets/src/beszel-system-grid/component.tsx index 68df8f207..3d5c3bd42 100644 --- a/packages/widgets/src/beszel-system-grid/component.tsx +++ b/packages/widgets/src/beszel-system-grid/component.tsx @@ -371,7 +371,7 @@ export default function BeszelSystemGridWidget({ isPending, } = clientApi.widget.beszel.getSystems.useQuery( { integrationIds }, - { staleTime: 5_000, refetchInterval: 5_000, retry: false }, + { staleTime: 10_000, gcTime: 48 * 60 * 60 * 1000, refetchInterval: 10_000, retry: false }, ); useBeszelSystemsSubscription(integrationIds, !isEditMode); diff --git a/packages/widgets/src/beszel-system-stats/component.tsx b/packages/widgets/src/beszel-system-stats/component.tsx index 4784877a5..feb0db5cc 100644 --- a/packages/widgets/src/beszel-system-stats/component.tsx +++ b/packages/widgets/src/beszel-system-stats/component.tsx @@ -67,7 +67,7 @@ export default function BeszelSystemStatsWidget({ data: systemsResult = [], isPending: systemsPending, error: systemsError, - } = clientApi.widget.beszel.getSystems.useQuery({ integrationIds }, { staleTime: 30 * 1000, retry: false }); + } = clientApi.widget.beszel.getSystems.useQuery({ integrationIds }, { staleTime: 10_000, gcTime: 48 * 60 * 60 * 1000, retry: false }); const systems = useMemo( () => systemsResult.flatMap((r) => r.systems.map((s) => ({ value: s.id, label: s.name }))), @@ -100,8 +100,9 @@ export default function BeszelSystemStatsWidget({ includeDocker: showDocker, }, { - staleTime: 30 * 1000, - refetchInterval: isLive ? false : 5_000, + staleTime: 10_000, + gcTime: 48 * 60 * 60 * 1000, + refetchInterval: isLive ? false : 10_000, enabled: !isLive && systemReady && selectedSystem !== "", retry: false, }, diff --git a/packages/widgets/src/beszel-system-table/component.tsx b/packages/widgets/src/beszel-system-table/component.tsx index 2604b5a0b..1d8c92355 100644 --- a/packages/widgets/src/beszel-system-table/component.tsx +++ b/packages/widgets/src/beszel-system-table/component.tsx @@ -76,7 +76,7 @@ export default function BeszelSystemTableWidget({ isPending, } = clientApi.widget.beszel.getSystems.useQuery( { integrationIds }, - { staleTime: 5_000, refetchInterval: 5_000, retry: false }, + { staleTime: 10_000, gcTime: 48 * 60 * 60 * 1000, refetchInterval: 10_000, retry: false }, ); const size = getSizeConfig(width); diff --git a/packages/widgets/src/beszel/_shared/system-stats-modal.tsx b/packages/widgets/src/beszel/_shared/system-stats-modal.tsx index 36d26ad1a..52781c5af 100644 --- a/packages/widgets/src/beszel/_shared/system-stats-modal.tsx +++ b/packages/widgets/src/beszel/_shared/system-stats-modal.tsx @@ -43,7 +43,7 @@ export const BeszelSystemStatsModal = createModal(( timePeriod, includeDocker: false, }, - { staleTime: 30_000 }, + { staleTime: 10_000, gcTime: 48 * 60 * 60 * 1000, refetchInterval: 10_000 }, ); const mappers = useMemo(