diff --git a/app.js b/app.js index cc9b40deaadda231f38d8bf40235be5a16d5ee49..dadd32531b1a2c227869ed5ede8a6c5fff9a6859 100644 --- a/app.js +++ b/app.js @@ -4,6 +4,45 @@ import cors from 'cors'; import { createRecord, dbConnection, findOneRecord } from './utils/handleDB.js'; import { middlewareErrorHandler, middlewareUnknownRoute } from './utils/handleErrors.js'; +/** + * start a performance observer + * watches for performance.mark entries + * + * each entry must follow the naming convention: + * performance.mark('<arbitaryName>_start'); + * performance.mark('<arbitaryName>_end'); + * + */ +const observer = new PerformanceObserver((list, observer) => { + for (const entry of list.getEntries()) { + try { + // check if mark is an endpoint + if (entry.name.endsWith('_end')) { + // define names, based on entry name + const measurementName = entry.name.replace('_end', ''); + const startName = `${measurementName}_start`; + // calculate + const measurement = performance.measure(measurementName, startName, entry.name); + // log + console.info({ name: measurement.name, duration: `${measurement.duration} ms` }); + + // clear + performance.clearMarks(entry.name); + performance.clearMarks(startName); + } + } catch (error) { + // log + console.error(error.message); + // clear + performance.clearMarks(entry.name); + } + + // observer.disconnect(); + + } +}); +observer.observe({ entryTypes: ['mark'] }); + /** * establish DB connection */ diff --git a/controllers/AI.js b/controllers/AI.js index 5e7e27bab77133372455944f86284e62a2131645..df2ef69798f130140b9a19631fcfbc65186b8215 100644 --- a/controllers/AI.js +++ b/controllers/AI.js @@ -4,6 +4,8 @@ import { aiDeleteModel, aiGetModels, aiGetModel, aiInstallModel, aiIsRunning, su import { mapStoredMessagesToChatMessages } from "@langchain/core/messages"; import { createRecord, findOneRecord, findRecordByID, findRecords } from '../utils/handleDB.js'; import { prefillDocumentObject } from '../utils/handleSchemes.js'; +import { performance } from "node:perf_hooks"; + @@ -81,10 +83,15 @@ export const deleteModel = async (req, res, next) => { */ export const getChat = async (req, res, next) => { // IF NO CHATID GIVEN + if (!req.body.chatId) { try { + console.info(performance); // create chat and remember ID + const markStart = performance.mark('create_chat_start'); req.body.chatId = await createChat(req.body.model, req.body.input); + const markEnd = performance.mark('create_chat_end'); + // console.log(performance.measure('createChat', 'create_chat_start', 'create_chat_end')); // return return next(); } catch (error) { diff --git a/controllers/Auth.js b/controllers/Auth.js index d184d4e915895cb05ef78e21bc3f3b76a8ffc2a8..6d6e9ab98de590dd9bbef542083b2263af92b55f 100644 --- a/controllers/Auth.js +++ b/controllers/Auth.js @@ -55,10 +55,9 @@ export const login = async (req, res, next) => { // check credentials try { // search for matching document + performance.mark('login_start'); foundUser = await findOneRecord(User, { email: req.body.email }, '+password'); - // console.log("🚀 ~ login ~ passwords:", req.body.password, foundUser.password); - // wrong login name if (!foundUser) { return res.status(401).json({ message: 'Unknown combination of login credentials.' }); @@ -69,17 +68,23 @@ export const login = async (req, res, next) => { return res.status(401).json({ message: 'Your account is still unverified. Check your emails for the verification link.' }); } - // check for correct password + // check for correct passwordperformance.mark('createAccessToken_start'); + performance.mark('bcryptComparePassword_start'); if (await bcrypt.compare(req.body.password, foundUser.password)) { + performance.mark('bcryptComparePassword_end'); // remember document but remove confidential info - // res.json({ message: foundUser._doc }); const user = hideConfidentialFields(User, foundUser); // create jsonwebtoken + performance.mark('createAccessToken_start'); const accessToken = createAccessToken({ id: user._id, role: user.role }); + performance.mark('createAccessToken_end'); + performance.mark('createRefreshToken_start'); const refreshToken = await createRefreshToken({ id: user._id }); + performance.mark('createRefreshToken_end'); if (refreshToken == null) return res.status(500).json({ message: 'Error creating refresh token' }); + performance.mark('login_end'); // success return res .cookie('refreshToken', refreshToken, { httpOnly: true, sameSite: 'none', secure: true }) diff --git a/ecosystem.config.cjs b/ecosystem.config.cjs index 1c778681ac802bd1306db7f107b033dbfd611374..1ef2312ebe2c4c5a808ac650da312b4feffaae53 100644 --- a/ecosystem.config.cjs +++ b/ecosystem.config.cjs @@ -2,10 +2,10 @@ module.exports = { apps: [{ name: "RAGchat-API", script: "server.js", - cwd: "/home/embruch/ragchat-api", - node_args: "--env-file=./.env", + cwd: "/local/embruch/ragchat-api", + node_args: "--env-file=/local/embruch/ragchat-api/.env", watch: true, - instances: "4", + instances: "8", exec_mode: "cluster", ignore_watch: ["./node_modules", "./RAGFiles", "./.git", "./coverage", "./logs", "./__tests__"], error_file: 'logs/error.log', diff --git a/utils/handleAI.js b/utils/handleAI.js index 1771ae7d19d52a870c16471fda743ef2dd8ab089..d025ea4ea1cccb291e98d2d41623b2fa300e57c5 100644 --- a/utils/handleAI.js +++ b/utils/handleAI.js @@ -193,5 +193,5 @@ export const chat = async (req, res, next) => { ]); // return the answer - return res.json({ answer: result.answer, chat }); + return res.json({ answer: result.answer, chat, performance: req.performance }); }; \ No newline at end of file diff --git a/utils/handleDB.js b/utils/handleDB.js index c1789ec3286ab65b9639239b1621ba9f3ccd6662..43b103faa4fd351a52e7347e9fb2662a4a45a87a 100644 --- a/utils/handleDB.js +++ b/utils/handleDB.js @@ -64,6 +64,7 @@ export const createRecord = async (model, data) => { * @return {object} found document */ export const findOneRecord = async (model, searchObject = {}, fieldHandler = '') => { + performance.mark('findOneRecord_start'); try { let foundRecord; if (!fieldHandler) { @@ -71,6 +72,7 @@ export const findOneRecord = async (model, searchObject = {}, fieldHandler = '') } else { foundRecord = await model.findOne(searchObject).select(fieldHandler); } + performance.mark('findOneRecord_end'); return foundRecord; } catch (error) { throw error; diff --git a/utils/handleSchemes.js b/utils/handleSchemes.js index 0ded4672174ee1f3f6216e7052d9883c1042bffe..ad631277d8bcd6ff53d8f78f8a19f4640755ade2 100644 --- a/utils/handleSchemes.js +++ b/utils/handleSchemes.js @@ -7,10 +7,12 @@ * @return {array} matching field names */ export const getConfidentialFields = (model) => { + performance.mark('getConfidentialFields_start'); const schema = Object.entries(model.schema.paths); const confidentialFields = schema.filter(function (field) { return field[1].selected === false; }); + performance.mark('getConfidentialFields_end'); return confidentialFields.map(field => field[0]); }; @@ -24,11 +26,13 @@ export const getConfidentialFields = (model) => { * @return {object} cleansed object */ export const hideConfidentialFields = (model, object) => { + performance.mark('hideConfidentialFields_start'); const confidentialFields = getConfidentialFields(model); // delete from object confidentialFields.forEach(field => { delete object[field]; }); + performance.mark('hideConfidentialFields_end'); return object; }; diff --git a/utils/handleTokens.js b/utils/handleTokens.js index fc50473b74cf40958e40b9b7ff01d1e59069c858..3bd5c4e0986336c33913166964eea4bff8b3b109 100644 --- a/utils/handleTokens.js +++ b/utils/handleTokens.js @@ -1,6 +1,8 @@ import jwt from 'jsonwebtoken'; import User from "../models/User.js"; import { findByIdAndUpdate, findOneAndUpdate, findOneRecord } from './handleDB.js'; +import { performance } from "node:perf_hooks"; + /** * generate a "oneTime" JWT, containing a given object