import "server-only"; import { redis } from "@/lib/redis"; type CacheEntry = { data: T; expiresAt: number }; const memory = new Map>(); /** * Redis-first cached query with an in-memory fallback. * Use for read-heavy endpoints polled by the browser (online count, etc.). */ export async function cached( key: string, ttlMs: number, fn: () => Promise, ): Promise { const ttlSec = Math.ceil(ttlMs / 1000); // Redis path (shared across instances). if (redis && redis.status !== "end") { try { const cached = await redis.get(key); if (cached !== null && cached !== undefined) { return JSON.parse(cached) as T; } } catch { /* fall through to DB / memory */ } } // In-memory fallback (single-instance fast path). const existing = memory.get(key); if (existing && existing.expiresAt > Date.now()) { return existing.data as T; } const data = await fn(); if (redis && redis.status !== "end") { try { await redis.setex(key, ttlSec, JSON.stringify(data)); } catch { /* non-critical: memory cache still works */ } } memory.set(key, { data, expiresAt: Date.now() + ttlMs }); return data; } /** Invalidate a single cache key across Redis + memory (call after writes). */ export async function uncached(key: string): Promise { memory.delete(key); if (redis && redis.status !== "end") { try { await redis.del(key); } catch { /* non-critical */ } } }