// ponytail: module-level cache is single-instance only. // Multi-instance deployments (REDIS_IS_EXTERNAL=true) bypass this entirely — // each request calls the upstream fetcher directly. // isMultiInstance is read at module load; bypass behavior is verified via code review, not unit tests. const isMultiInstance = process.env.REDIS_IS_EXTERNAL === "true"; interface CachedEntry { data: T; timestamp: number; pending?: Promise; } const cache = new Map>(); const DEFAULT_TTL_MS = 60_000; const MAX_ENTRIES = 200; const cacheKey = (integrationId: string, queryKey: string) => `${integrationId}:${queryKey}`; export const getCachedIntegrationData = async ( integrationId: string, queryKey: string, fetcher: () => Promise, ttlMs = DEFAULT_TTL_MS, ): Promise => { if (isMultiInstance) { return fetcher(); } const key = cacheKey(integrationId, queryKey); const existing = cache.get(key) as CachedEntry | undefined; const now = Date.now(); if (existing) { if (existing.pending) { return existing.pending; } if (existing.timestamp > 0 && now - existing.timestamp < ttlMs) { return existing.data; } } const pending = fetcher() .then((data) => { cache.set(key, { data, timestamp: Date.now() }); if (cache.size > MAX_ENTRIES) { let oldest: string | undefined; let oldestTime = Infinity; for (const [k, v] of cache) { if (v.timestamp < oldestTime) { oldestTime = v.timestamp; oldest = k; } } if (oldest) cache.delete(oldest); } return data; }) .catch((error) => { const entry = cache.get(key) as CachedEntry | undefined; if (entry) { delete entry.pending; if (entry.timestamp > 0) return entry.data; } cache.delete(key); throw error; }); if (existing) { existing.pending = pending; return existing.data; } cache.set(key, { data: undefined as T, timestamp: 0, pending }); return pending; }; export const invalidateIntegrationDataCache = (integrationId: string) => { for (const [key] of cache) { if (key.startsWith(`${integrationId}:`)) { cache.delete(key); } } };