Studentify database clients and website API adapters

This commit is contained in:
Hikyu 2026-07-10 09:30:41 +02:00
parent 9c719b96b3
commit a35e6a99cb

View File

@ -10,13 +10,6 @@ const arangoKv = arango.collection("kv");
type Timing = { op: string; key: string; ms: number }; type Timing = { op: string; key: string; ms: number };
type Adapter = {
timings: Timing[];
getObject: (key: string) => Promise<any>;
getField: (key: string, field: string) => Promise<any>;
getIds: (key: string) => Promise<string[]>;
};
function stringifyObj(o: any) { function stringifyObj(o: any) {
if (!o) return null; if (!o) return null;
const out: any = {}; const out: any = {};
@ -24,74 +17,104 @@ function stringifyObj(o: any) {
return out; return out;
} }
function redisAdapter(): Adapter { function getRedisAdapter() {
const timings: Timing[] = []; const timings: Timing[] = [];
const track = async <T>(op: string, key: string, fn: () => Promise<T>): Promise<T> => {
const start = performance.now();
const res = await fn();
timings.push({ op, key, ms: performance.now() - start });
return res;
};
return { return {
timings, timings,
getObject: (key) => async getObject(key: string) {
track("hgetall", key, async () => { const start = performance.now();
const o = await redis.hgetall(key); const o = await redis.hgetall(key);
return o && Object.keys(o).length ? o : null; timings.push({ op: "hgetall", key, ms: performance.now() - start });
}), return o && Object.keys(o).length ? o : null;
getField: (key, field) => track("hget", key, () => redis.hget(key, field)), },
getIds: (key) => track("smembers", key, async () => (await redis.smembers(key)) || []), async getField(key: string, field: string) {
const start = performance.now();
const res = await redis.hget(key, field);
timings.push({ op: "hget", key, ms: performance.now() - start });
return res;
},
async getIds(key: string) {
const start = performance.now();
const res = await redis.smembers(key);
timings.push({ op: "smembers", key, ms: performance.now() - start });
return res || [];
}
}; };
} }
function memcachedAdapter(): Adapter { function getMemcachedAdapter() {
const timings: Timing[] = []; const timings: Timing[] = [];
const getJson = async (key: string) => {
async function getJson(key: string) {
const { value } = await memcached.get(key); const { value } = await memcached.get(key);
return value ? JSON.parse(value.toString()) : null; return value ? JSON.parse(value.toString()) : null;
}; }
const track = async <T>(op: string, key: string, fn: () => Promise<T>): Promise<T> => {
const start = performance.now();
const res = await fn();
timings.push({ op, key, ms: performance.now() - start });
return res;
};
return { return {
timings, timings,
getObject: (key) => track("get", key, async () => stringifyObj(await getJson(key))), async getObject(key: string) {
getField: (key, field) => track("get", key, async () => (await getJson(key))?.[field] ?? null), const start = performance.now();
getIds: (key) => track("get", key, async () => ((await getJson(key)) || []).map(String)), const res = await getJson(key);
timings.push({ op: "get", key, ms: performance.now() - start });
return stringifyObj(res);
},
async getField(key: string, field: string) {
const start = performance.now();
const res = await getJson(key);
timings.push({ op: "get", key, ms: performance.now() - start });
return res ? res[field] : null;
},
async getIds(key: string) {
const start = performance.now();
const res = await getJson(key);
timings.push({ op: "get", key, ms: performance.now() - start });
return res ? res.map(String) : [];
}
}; };
} }
function arangoAdapter(): Adapter { function getArangoAdapter() {
const timings: Timing[] = []; const timings: Timing[] = [];
const getVal = async (key: string) => {
async function getVal(key: string) {
const doc = await arangoKv.document(key, { graceful: true }); const doc = await arangoKv.document(key, { graceful: true });
return doc ? doc.value : null; return doc ? doc.value : null;
}; }
const track = async <T>(op: string, key: string, fn: () => Promise<T>): Promise<T> => {
const start = performance.now();
const res = await fn();
timings.push({ op, key, ms: performance.now() - start });
return res;
};
return { return {
timings, timings,
getObject: (key) => track("document", key, async () => stringifyObj(await getVal(key))), async getObject(key: string) {
getField: (key, field) => track("document", key, async () => (await getVal(key))?.[field] ?? null), const start = performance.now();
getIds: (key) => track("document", key, async () => ((await getVal(key)) || []).map(String)), const res = await getVal(key);
timings.push({ op: "document", key, ms: performance.now() - start });
return stringifyObj(res);
},
async getField(key: string, field: string) {
const start = performance.now();
const res = await getVal(key);
timings.push({ op: "document", key, ms: performance.now() - start });
return res ? res[field] : null;
},
async getIds(key: string) {
const start = performance.now();
const res = await getVal(key);
timings.push({ op: "document", key, ms: performance.now() - start });
return res ? res.map(String) : [];
}
}; };
} }
function getAdapter(db: string): Adapter { function getAdapter(db: string) {
if (db === "memcached") return memcachedAdapter(); if (db === "memcached") return getMemcachedAdapter();
if (db === "arangodb") return arangoAdapter(); if (db === "arangodb") return getArangoAdapter();
return redisAdapter(); return getRedisAdapter();
} }
function timingSummary(timings: Timing[]) { function timingSummary(timings: Timing[]) {
const total = timings.reduce((s, t) => s + t.ms, 0); let total = 0;
for (const t of timings) {
total += t.ms;
}
return { count: timings.length, total, ops: timings }; return { count: timings.length, total, ops: timings };
} }