import { Bulk, connect, Redis, RedisConnectOptions, RedisValue, } from "https://deno.land/x/redis@v0.31.0/mod.ts"; import { createLogger } from "@lib/log.ts"; const REDIS_HOST = Deno.env.get("REDIS_HOST"); const REDIS_PASS = Deno.env.get("REDIS_PASS") || ""; const REDIS_PORT = Deno.env.get("REDIS_PORT"); const log = createLogger("cache"); async function createCache(): Promise { if (REDIS_HOST) { const conf: RedisConnectOptions = { hostname: REDIS_HOST, port: REDIS_PORT || 6379, maxRetryCount: 2, }; if (REDIS_PASS) { conf.password = REDIS_PASS; } try { const client = await connect(conf); log.info("redis connected"); return client; } catch (_err) { log.info("cant connect to redis, falling back to mock"); } } const mockRedis = new Map(); return { async keys() { return mockRedis.keys(); }, async delete(key: string) { mockRedis.delete(key); return key; }, async set(key: string, value: RedisValue) { mockRedis.set(key, value); return value.toString(); }, async get(key: string) { return mockRedis.get(key) as Bulk; }, }; } const cache = await createCache(); export async function get(id: string, binary = false) { if (binary && !(cache instanceof Map)) { const cacheHit = await cache.sendCommand("GET", [id], { returnUint8Arrays: true, }) as T; return cacheHit; } const cacheHit = await cache.get(id) as T; return cacheHit; } export function clearAll() { if ("flushall" in cache) { return cache.flushall(); } else { for (const k of cache.keys()) { cache.delete(k); } } } export function expire(id: string, seconds: number) { if ("expire" in cache) { return cache.expire(id, seconds); } } type RedisOptions = { expires?: number; }; export function del(key: string) { return cache.del(key); } export function keys(prefix: string) { return cache.keys(prefix); } export function set( id: string, content: T, options?: RedisOptions, ) { log.debug("storing ", { id }); return cache.set(id, content, { ex: options?.expires || undefined }); } export const cacheFunction = async Promise)>( { fn, id, options = {}, }: { fn: T; id: string; options?: RedisOptions; }, ): Promise>> => { const cacheResult = await get(id) as string; if (cacheResult) { return JSON.parse(cacheResult) as Awaited>; } const result = await fn(); set(id, JSON.stringify(result), options); return result as Awaited>; };