memorium/lib/cache/cache.ts

127 lines
2.7 KiB
TypeScript
Raw Normal View History

import {
2023-08-04 13:48:12 +02:00
Bulk,
connect,
Redis,
RedisConnectOptions,
RedisValue,
} from "https://deno.land/x/redis@v0.31.0/mod.ts";
2023-08-05 22:16:14 +02:00
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");
2023-08-05 22:16:14 +02:00
const log = createLogger("cache");
2023-08-04 22:35:25 +02:00
async function createCache<T>(): Promise<Redis> {
if (REDIS_HOST) {
const conf: RedisConnectOptions = {
hostname: REDIS_HOST,
port: REDIS_PORT || 6379,
2023-08-04 13:48:12 +02:00
maxRetryCount: 2,
};
if (REDIS_PASS) {
conf.password = REDIS_PASS;
}
2023-08-04 13:48:12 +02:00
try {
const client = await connect(conf);
2023-08-05 22:16:14 +02:00
log.info("redis connected");
2023-08-04 13:48:12 +02:00
return client;
} catch (_err) {
2023-08-05 22:16:14 +02:00
log.info("cant connect to redis, falling back to mock");
2023-08-04 13:48:12 +02:00
}
}
2023-08-04 13:48:12 +02:00
const mockRedis = new Map<string, RedisValue>();
return {
2023-08-04 22:35:25 +02:00
async keys() {
return mockRedis.keys();
},
async delete(key: string) {
mockRedis.delete(key);
return key;
},
2023-08-04 13:48:12 +02:00
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<T>(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;
}
2023-08-01 18:35:35 +02:00
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);
}
}
2023-08-02 01:58:03 +02:00
type RedisOptions = {
expires?: number;
};
2023-08-04 22:35:25 +02:00
export function del(key: string) {
return cache.del(key);
}
export function keys(prefix: string) {
return cache.keys(prefix);
}
export function set<T extends RedisValue>(
2023-08-02 01:58:03 +02:00
id: string,
content: T,
options?: RedisOptions,
) {
2023-08-05 22:16:14 +02:00
log.debug("storing ", { id });
return cache.set(id, content, { ex: options?.expires || undefined });
}
2023-08-09 23:51:40 +02:00
export const cacheFunction = async <T extends (() => Promise<unknown>)>(
{
fn,
id,
options = {},
}: {
fn: T;
id: string;
options?: RedisOptions;
},
): Promise<Awaited<ReturnType<T>>> => {
const cacheResult = await get(id) as string;
if (cacheResult) {
return JSON.parse(cacheResult) as Awaited<ReturnType<typeof fn>>;
}
const result = await fn();
set(id, JSON.stringify(result), options);
return result as Awaited<ReturnType<typeof fn>>;
};