Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/apps/api/src/analytics-overview-cache.ts
T

62 lines
1.9 KiB
TypeScript

import type { AnalyticsOverview, AnalyticsPeriod, ApiAppDependencies } from "./app-dependencies.js";
export const PUBLIC_ANALYTICS_OVERVIEW_CACHE_TTL_MS = 5_000;
export type PublicAnalyticsOverviewScope = "platform-global";
type PublicAnalyticsOverviewDependencies = Pick<ApiAppDependencies, "getAnalyticsOverview">;
type CacheEntry = {
dependencies: PublicAnalyticsOverviewDependencies;
expiresAt: number;
value?: AnalyticsOverview;
pending?: Promise<AnalyticsOverview>;
};
type CacheKey = `${PublicAnalyticsOverviewScope}:${AnalyticsPeriod}`;
const cache = new Map<CacheKey, CacheEntry>();
/**
* Coalesces simultaneous public dashboard reads and keeps the expensive
* aggregate query off Postgres for a short, bounded freshness window.
*/
export async function getCachedPublicAnalyticsOverview(input: {
dependencies: PublicAnalyticsOverviewDependencies;
scope: PublicAnalyticsOverviewScope;
period: AnalyticsPeriod;
now?: number;
}): Promise<AnalyticsOverview> {
const now = input.now ?? Date.now();
const key: CacheKey = `${input.scope}:${input.period}`;
const current = cache.get(key);
if (current?.dependencies === input.dependencies && current.expiresAt > now) {
if (current.value) return current.value;
if (current.pending) return current.pending;
}
const pending = input.dependencies.getAnalyticsOverview({ period: input.period, portalId: null });
const next: CacheEntry = {
dependencies: input.dependencies,
expiresAt: now + PUBLIC_ANALYTICS_OVERVIEW_CACHE_TTL_MS,
pending,
};
cache.set(key, next);
try {
const value = await pending;
if (cache.get(key) === next) {
next.value = value;
next.pending = undefined;
}
return value;
} catch (error) {
if (cache.get(key) === next) {
cache.delete(key);
}
throw error;
}
}
export function resetPublicAnalyticsOverviewCacheForTests(): void {
cache.clear();
}