Skip to content

Commit

Permalink
fix redis caching
Browse files Browse the repository at this point in the history
  • Loading branch information
Larsundso committed Sep 30, 2024
1 parent 419a8f2 commit e2062c6
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 243 deletions.
183 changes: 176 additions & 7 deletions src/BaseClient/Bot/DataBase.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
/* eslint-disable @typescript-eslint/no-explicit-any */

import { Prisma, PrismaClient } from '@prisma/client';
import type {
DefaultArgs,
DynamicQueryExtensionCb,
InternalArgs,
} from '@prisma/client/runtime/library.js';
import type { DataBaseTables, MaybeArray, RequiredOnly } from 'src/Typings/Typings.js';
import metricsCollector from './Metrics.js';

import logchannels from './DataBase/logchannels.js';
import guildsettings from './DataBase/guildsettings.js';
import customclients from './DataBase/customclients.js';
import Redis from './Redis.js';

const prisma = new PrismaClient();

Expand All @@ -27,9 +30,15 @@ export default prisma
.$extends({
name: 'Cache Middleware',
query: {
...(logchannels as any),
...(guildsettings as any),
...(customclients as any),
guildsettings: {
$allOperations: async (data) => handleOperation('guildsettings', 'guildid', data),
},
logchannels: {
$allOperations: async (data) => handleOperation('logchannels', 'guildid', data),
},
customclients: {
$allOperations: async (data) => handleOperation('customclients', 'guildid', data),
},
},
})
.$extends({
Expand All @@ -46,3 +55,163 @@ export default prisma
},
},
});

type Operations<T extends keyof Prisma.TypeMap['model']> = Prisma.TypeMap['model'][T]['operations'];
type Args<
T extends keyof Prisma.TypeMap['model'],
K extends keyof Operations<T>,
> = Operations<T>[K];

export const getKey = <T extends keyof Prisma.TypeMap['model']>(
where: Args<T, 'findMany'>['args']['where'] | Prisma.StringFilter | undefined,
keyName: keyof RequiredOnly<Args<T, 'findUnique'>['args']['where']>,
): string[] | null => {
if (!where) return null;
if (!(keyName in where)) return null;

// @ts-expect-error
const keyVal = where[keyName as keyof typeof where];

if (!keyVal) return null;
if (typeof keyVal === 'string') return [keyVal];
if (typeof keyVal !== 'object') return null;
if ('in' in keyVal && keyVal.in) {
if (!Array.isArray(keyVal.in)) return null;
return keyVal.in;
}

return null;
};

export const handleFind = async <T extends keyof DataBaseTables>(
data: Parameters<
DynamicQueryExtensionCb<
Prisma.TypeMap<InternalArgs & DefaultArgs, Prisma.PrismaClientOptions>,
'model',
T,
| 'findUnique'
| 'findUniqueOrThrow'
| 'findFirst'
| 'findFirstOrThrow'
| 'findMany'
| 'create'
| 'createMany'
| 'createManyAndReturn'
| 'delete'
| 'update'
| 'deleteMany'
| 'updateMany'
| 'upsert'
| 'aggregate'
| 'groupBy'
| 'count'
>
>[0],
keys: string[],
tableName: T,
keyName: keyof DataBaseTables[T],
) => {
const cached = await Promise.all(keys.map((key) => Redis.get(key)));

if (cached.some((c) => c === null) || !cached.length) {
return cacheNewEntry(
(await data.query(data.args as Parameters<typeof data.query>[0])) as MaybeArray<
DataBaseTables[T]
>,
tableName,
keyName,
);
}

if (!isValid(cached as string[])) {
return data.query(data.args as Parameters<typeof data.query>[0]);
}

const validCached = (cached as string[]).map((c) => JSON.parse(c) as DataBaseTables[T]);

return ['findUnique', 'findFirst'].includes(data.operation) ? validCached[0] : validCached;
};

export const cacheNewEntry = <T extends keyof DataBaseTables>(
res: MaybeArray<DataBaseTables[T]>,
tableName: T,
keyName: keyof DataBaseTables[T],
) => {
if ((Array.isArray(res) && !res.length) || !res) return res;

if (Array.isArray(res)) {
res
.filter((r) => !!r[keyName])
.forEach((r) =>
Redis.set(`${process.env.mainId}:${tableName}:${r[keyName]}`, JSON.stringify(r)),
);
} else if (res[keyName]) {
Redis.set(`${process.env.mainId}:${tableName}:${res[keyName]}`, JSON.stringify(res));
}

return res;
};

export const isValid = (cached: string[]) => {
try {
cached.forEach((c) => JSON.parse(c));
return true;
} catch {
return false;
}
};

export const handleOperation = <T extends keyof Prisma.TypeMap['model'] & keyof DataBaseTables>(
name: T,
index: keyof RequiredOnly<Args<T, 'findUnique'>['args']['where']> & keyof DataBaseTables[T],
data: Parameters<
DynamicQueryExtensionCb<
Prisma.TypeMap<InternalArgs & DefaultArgs, Prisma.PrismaClientOptions>,
'model',
T,
| 'findUnique'
| 'findUniqueOrThrow'
| 'findFirst'
| 'findFirstOrThrow'
| 'findMany'
| 'create'
| 'createMany'
| 'createManyAndReturn'
| 'delete'
| 'update'
| 'deleteMany'
| 'updateMany'
| 'upsert'
| 'aggregate'
| 'groupBy'
| 'count'
>
>[0],
) => {
if (!('where' in data.args) || !data.args.where) return data.query(data.args);
if (!data.args.where) return data.query(data.args);

const indexValues = getKey<typeof name>(data.args.where, index);

if (!indexValues?.length) return data.query(data.args);
const keys = indexValues.map((value) => `${process.env.mainId}:${name}:${value}`);

switch (data.operation) {
case 'findMany':
case 'findFirst':
case 'findUnique':
return handleFind(data, keys, name, index);
case 'update':
case 'updateMany':
case 'upsert':
case 'create':
case 'delete':
case 'deleteMany':
case 'createMany':
case 'createManyAndReturn':
keys.forEach((key) => Redis.del(key));
return data.query(data.args);
default:
return data.query(data.args);
}
};
43 changes: 0 additions & 43 deletions src/BaseClient/Bot/DataBase/customclients.ts

This file was deleted.

150 changes: 0 additions & 150 deletions src/BaseClient/Bot/DataBase/guildsettings.ts

This file was deleted.

Loading

0 comments on commit e2062c6

Please sign in to comment.