From b3692542b3f15037095271406df5663f83b359c7 Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 16 Jul 2022 19:01:36 +0200 Subject: [PATCH] did minor trolling --- lib/src/misc/database.ts | 112 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 lib/src/misc/database.ts diff --git a/lib/src/misc/database.ts b/lib/src/misc/database.ts new file mode 100644 index 0000000..df144c2 --- /dev/null +++ b/lib/src/misc/database.ts @@ -0,0 +1,112 @@ +/** + * Mongodb wrapper that uses redis for caching. I found out I don't + * actually need this but I already wrote it so I'm keeping it here + * + * I also don't know how well it works + */ + +import { FindOneResult, ICollection, IObjectID } from 'monk'; +import { createClient } from 'redis'; + +// createClient() returns a wacky ass type and this is the only way I can think of to get it +const _redisClient = createClient(); +type RedisClient = typeof _redisClient; + +class CachedDb { + db: ICollection; + redis: RedisClient; + ttl: number = 300; + redisPrefix: string; + mongoIndexKey: string; + debug: boolean = false; + + constructor(db: ICollection, redis: RedisClient, indexKey: string) { + this.db = db; + this.redis = redis; + this.redisPrefix = db.name; + this.mongoIndexKey = indexKey; + } + + /** + * Gets a database entry and returns it + * @param key + */ + get(key: string): Promise<({ _id: string | IObjectID } & T) | T | undefined> { + return new Promise(async (resolve, reject) => { + try { + const res = await this.redis.GET(`${this.redisPrefix}:${key}`); + if (res) { + if (this.debug) console.debug('Db: Answering from cache'); + resolve(JSON.parse(res)); + return; + } + else if (this.debug) console.debug('Db: Fetching from database'); + + const mongoRes = await this.db.findOne({ [this.mongoIndexKey]: key } as any); + resolve(mongoRes ?? undefined); + + try { + await this.redis.SET(`${this.redisPrefix}:${key}`, JSON.stringify(mongoRes)); + await this.redis.EXPIRE(`${this.redisPrefix}:${key}`, this.ttl); + } catch(e) { + console.warn('Failed to cache to redis:', e); + } + } catch(e) { reject(e) } + }); + } + + /** + * Inserts or updates a database entry and caches it. Overwrites existing keys + * @param value + */ + set(value: T): Promise<{ ok: number; n: number; nModified: number; }> { + return new Promise(async (resolve, reject) => { + const key = (value as any)[this.mongoIndexKey]; + try { + const res = await this.db.update( + { [this.mongoIndexKey]: key } as any, + value, + { upsert: true, replace: true, single: true } + ); + resolve(res); + if (!res.ok) return console.debug('Db: Not caching;', res); + } catch(e) { reject(e) } + + try { + if (this.debug) console.debug('Db: Caching updated document'); + await this.redis.SET(`${this.redisPrefix}:${key}`, JSON.stringify(value)); + await this.redis.EXPIRE(`${this.redisPrefix}:${key}`, this.ttl); + } catch(e) { + console.warn('Failed to cache to redis:', e); + } + }); + } + + /** + * Inserts or updates a database entry without overwriting other keys + * @param value + * @returns The updated/created document + */ + update(key: string, value: Partial): Promise> { + return new Promise(async (resolve, reject) => { + try { + const res = await this.db.findOneAndUpdate( + { [this.mongoIndexKey]: key } as any, + { $set: value }, + { upsert: true, returnOriginal: true } // monk is a steaming pile of garbage, i need to set the deprecated returnOriginal + ); // because the fuckwarts who wrote it made it default to `false` + resolve(res); + if (!res) return; + + try { + await this.redis.SET(`${this.redisPrefix}:${key}`, JSON.stringify(res)); + await this.redis.EXPIRE(`${this.redisPrefix}:${key}`, this.ttl); + } catch(e) { + console.warn('Failed to cache to redis:', e); + } + } catch(e) { reject(e) } + }); + } +} + +export { CachedDb }