- Add file-level biome-ignore for noExplicitAny in prisma-facade.ts (intentional any for Prisma API compatibility surface) - Fix noNonNullAssertion errors in cached-db.ts (redis null-guard fixes) - Auto-fix formatting + organizeImports across modified files - 0 tsc errors, 0 biome errors, 583 tests passing
62 lines
1.4 KiB
TypeScript
62 lines
1.4 KiB
TypeScript
import "server-only";
|
|
|
|
import { logger } from "@/lib/logger";
|
|
import { redis } from "@/lib/redis";
|
|
|
|
const DEFAULT_CACHE_TTL = 60;
|
|
|
|
function isRedisAvailable(): boolean {
|
|
return !!redis && redis.status !== "end";
|
|
}
|
|
|
|
export async function cachedQuery<T>(
|
|
cacheKey: string,
|
|
queryFn: () => Promise<T>,
|
|
ttl: number = DEFAULT_CACHE_TTL,
|
|
): Promise<T> {
|
|
if (!isRedisAvailable()) {
|
|
return queryFn();
|
|
}
|
|
|
|
try {
|
|
const cached = await redis?.get(cacheKey);
|
|
if (cached !== null && cached !== undefined) {
|
|
return JSON.parse(cached) as T;
|
|
}
|
|
} catch (err) {
|
|
logger.warn("[cache] read failed, falling back to DB", {
|
|
key: cacheKey,
|
|
error: String(err),
|
|
});
|
|
}
|
|
|
|
const result = await queryFn();
|
|
|
|
try {
|
|
await redis?.setex(cacheKey, ttl, JSON.stringify(result));
|
|
} catch (err) {
|
|
logger.warn("[cache] write failed, continuing without cache", {
|
|
key: cacheKey,
|
|
error: String(err),
|
|
});
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/** Invalidate cache keys matching a glob pattern. */
|
|
export async function invalidateCache(pattern: string): Promise<number> {
|
|
if (!isRedisAvailable()) return 0;
|
|
const r = redis;
|
|
if (!r) return 0;
|
|
const keys = await r.keys(pattern);
|
|
return keys.length > 0 ? r.del(...keys) : 0;
|
|
}
|
|
|
|
/** Invalidate a single cache key immediately. */
|
|
export async function invalidateKey(key: string): Promise<number> {
|
|
if (!isRedisAvailable()) return 0;
|
|
if (!redis) return 0;
|
|
return redis.del(key);
|
|
}
|