From a38248217303413fd9bb14bd96c79801b43c60b2 Mon Sep 17 00:00:00 2001 From: mikiher Date: Sun, 10 Nov 2024 08:34:47 +0200 Subject: [PATCH] Add in-memory user cache --- server/Auth.js | 30 ++++------- server/models/User.js | 112 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 117 insertions(+), 25 deletions(-) diff --git a/server/Auth.js b/server/Auth.js index 5b2d8bcd..b0046799 100644 --- a/server/Auth.js +++ b/server/Auth.js @@ -990,28 +990,18 @@ class Auth { }) } } - - Database.userModel - .update( - { - pash: pw - }, - { - where: { id: matchingUser.id } - } - ) - .then(() => { - Logger.info(`[Auth] User "${matchingUser.username}" changed password`) - res.json({ - success: true - }) + try { + await matchingUser.update({ pash: pw }) + Logger.info(`[Auth] User "${matchingUser.username}" changed password`) + res.json({ + success: true }) - .catch((error) => { - Logger.error(`[Auth] User "${matchingUser.username}" failed to change password`, error) - res.json({ - error: 'Unknown error' - }) + } catch (error) { + Logger.error(`[Auth] User "${matchingUser.username}" failed to change password`, error) + res.json({ + error: 'Unknown error' }) + } } } diff --git a/server/models/User.js b/server/models/User.js index 906a7d68..259f841c 100644 --- a/server/models/User.js +++ b/server/models/User.js @@ -3,6 +3,53 @@ const sequelize = require('sequelize') const Logger = require('../Logger') const SocketAuthority = require('../SocketAuthority') const { isNullOrNaN } = require('../utils') +const { LRUCache } = require('lru-cache') + +class UserCache { + constructor() { + this.cache = new LRUCache({ max: 100 }) + } + + getById(id) { + const user = this.cache.get(id) + return user + } + + getByEmail(email) { + const user = this.cache.find((u) => u.email === email) + return user + } + + getByUsername(username) { + const user = this.cache.find((u) => u.username === username) + return user + } + + getByOldId(oldUserId) { + const user = this.cache.find((u) => u.extraData?.oldUserId === oldUserId) + return user + } + + getByOpenIDSub(sub) { + const user = this.cache.find((u) => u.extraData?.authOpenIDSub === sub) + return user + } + + set(user) { + user.fromCache = true + this.cache.set(user.id, user) + } + + delete(userId) { + this.cache.delete(userId) + } + + maybeInvalidate(user) { + if (!user.fromCache) this.delete(user.id) + } +} + +const userCache = new UserCache() const { DataTypes, Model } = sequelize @@ -206,7 +253,11 @@ class User extends Model { */ static async getUserByUsername(username) { if (!username) return null - return this.findOne({ + + const cachedUser = userCache.getByUsername(username) + if (cachedUser) return cachedUser + + const user = await this.findOne({ where: { username: { [sequelize.Op.like]: username @@ -214,6 +265,10 @@ class User extends Model { }, include: this.sequelize.models.mediaProgress }) + + if (user) userCache.set(user) + + return user } /** @@ -223,7 +278,11 @@ class User extends Model { */ static async getUserByEmail(email) { if (!email) return null - return this.findOne({ + + const cachedUser = userCache.getByEmail(email) + if (cachedUser) return cachedUser + + const user = await this.findOne({ where: { email: { [sequelize.Op.like]: email @@ -231,6 +290,10 @@ class User extends Model { }, include: this.sequelize.models.mediaProgress }) + + if (user) userCache.set(user) + + return user } /** @@ -240,9 +303,17 @@ class User extends Model { */ static async getUserById(userId) { if (!userId) return null - return this.findByPk(userId, { + + const cachedUser = userCache.getById(userId) + if (cachedUser) return cachedUser + + const user = await this.findByPk(userId, { include: this.sequelize.models.mediaProgress }) + + if (user) userCache.set(user) + + return user } /** @@ -254,12 +325,19 @@ class User extends Model { */ static async getUserByIdOrOldId(userId) { if (!userId) return null - return this.findOne({ + const cachedUser = userCache.getById(userId) || userCache.getByOldId(userId) + if (cachedUser) return cachedUser + + const user = await this.findOne({ where: { [sequelize.Op.or]: [{ id: userId }, { 'extraData.oldUserId': userId }] }, include: this.sequelize.models.mediaProgress }) + + if (user) userCache.set(user) + + return user } /** @@ -269,10 +347,18 @@ class User extends Model { */ static async getUserByOpenIDSub(sub) { if (!sub) return null - return this.findOne({ + + const cachedUser = userCache.getByOpenIDSub(sub) + if (cachedUser) return cachedUser + + const user = await this.findOne({ where: sequelize.where(sequelize.literal(`extraData->>"authOpenIDSub"`), sub), include: this.sequelize.models.mediaProgress }) + + if (user) userCache.set(user) + + return user } /** @@ -623,6 +709,7 @@ class User extends Model { mediaProgress = await this.sequelize.models.mediaProgress.create(newMediaProgressPayload) this.mediaProgresses.push(mediaProgress) } + userCache.maybeInvalidate(this) return { mediaProgress } @@ -804,6 +891,21 @@ class User extends Model { return hasUpdates } + + async update(values, options) { + userCache.maybeInvalidate(this) + return await super.update(values, options) + } + + async save(options) { + userCache.maybeInvalidate(this) + return await super.save(options) + } + + async destroy(options) { + userCache.delete(this.id) + await super.destroy(options) + } } module.exports = User