DeepSeek feat: extract performance monitoring into an opt-in tool and improve error handling in teacher routes

This commit is contained in:
krizam
2026-10-30 22:29:33 +05:30
parent fcc1d7ecaf
commit 536fdc7e84
9 changed files with 291 additions and 188 deletions
+6
View File
@@ -0,0 +1,6 @@
// Opt-in loader: `node --import ./tools/perf-hooks.mjs src/index.ts` (see `yarn perf`).
// Keeps all performance instrumentation out of src/.
import { register, requestTimer } from './performance.ts';
register();
requestTimer();
+106 -36
View File
@@ -1,61 +1,131 @@
import { performance } from "node:perf_hooks";
/**
* Opt-in performance harness. Nothing in src/ imports this file, so the app
* runs without it. Load it with `yarn perf` (or `node --import ./tools/perf-hooks.mjs`).
*/
import mongoose from "mongoose";
import { monitorEventLoopDelay, performance } from "node:perf_hooks";
import express from "express";
type DbMetric = {
type Metric = {
count: number;
totalMs: number;
maxMs: number;
over100Ms: number;
};
const metrics = new Map<string, DbMetric>();
type RequestMetric = {
count: number;
totalMs: number;
maxMs: number;
over500Ms: number;
};
export async function measureDb<T>(
label: string,
operation: () => Promise<T>,
): Promise<T> {
const started = performance.now();
const dbMetrics = new Map<string, Metric>();
const requestMetrics = new Map<string, RequestMetric>();
try {
return await operation();
} finally {
const durationMs = performance.now() - started;
const eventLoop = monitorEventLoopDelay({ resolution: 20 });
eventLoop.enable();
let metric = metrics.get(label);
if (!metric) {
metric = { count: 0, totalMs: 0, maxMs: 0, over100Ms: 0 };
metrics.set(label, metric);
}
// --- Database timings -------------------------------------------------------
// Mongoose emits these for every query, so no route code has to be touched.
export function register() {
mongoose.connection.on('connected', () => {
const client = mongoose.connection.getClient();
metric.count++;
metric.totalMs += durationMs;
metric.maxMs = Math.max(metric.maxMs, durationMs);
client.on('commandStarted', (event: any) => {
(event as any).__startedAt = performance.now();
});
if (durationMs > 100) {
metric.over100Ms++;
}
}
client.on('commandSucceeded', (event: any) => {
recordDb(`${event.commandName}.${Object.keys(event.command ?? {})[0] ?? 'unknown'}`, event);
});
client.on('commandFailed', (event: any) => {
recordDb(`${event.commandName}.failed`, event);
});
});
}
setInterval(() => {
if (metrics.size === 0) return;
function recordDb(label: string, event: any) {
const startedAt = event.__startedAt;
if (typeof startedAt !== 'number') return;
const operations = Object.fromEntries(
[...metrics.entries()].map(([label, metric]) => [
label,
const durationMs = performance.now() - startedAt;
let metric = dbMetrics.get(label);
if (!metric) {
metric = { count: 0, totalMs: 0, maxMs: 0, over100Ms: 0 };
dbMetrics.set(label, metric);
}
metric.count++;
metric.totalMs += durationMs;
metric.maxMs = Math.max(metric.maxMs, durationMs);
if (durationMs > 100) metric.over100Ms++;
}
// --- Request timings --------------------------------------------------------
// Installed by patching express.json() from the loader, so index.ts stays clean.
export function requestTimer() {
const originalJson = express.json.bind(express);
express.json = ((...args: any[]) => {
const parser = (originalJson as any)(...args);
return (req: any, res: any, next: any) => {
const started = performance.now();
res.on("finish", () => {
const durationMs = performance.now() - started;
const key = `${req.method} ${req.route?.path ?? req.path} ${res.statusCode}`;
let metric = requestMetrics.get(key);
if (!metric) {
metric = { count: 0, totalMs: 0, maxMs: 0, over500Ms: 0 };
requestMetrics.set(key, metric);
}
const current = metric;
current.count++;
current.totalMs += durationMs;
current.maxMs = Math.max(current.maxMs, durationMs);
if (durationMs > 500) current.over500Ms++;
});
parser(req, res, next);
};
}) as typeof express.json;
}
// --- Reporting --------------------------------------------------------------
function summarize<T extends { count: number; totalMs: number; maxMs: number }>(entries: [string, T][]) {
return Object.fromEntries(
entries.map(([key, value]) => [
key,
{
count: metric.count,
avgMs: Number((metric.totalMs / metric.count).toFixed(2)),
maxMs: Number(metric.maxMs.toFixed(2)),
over100Ms: metric.over100Ms,
count: value.count,
avgMs: Number((value.totalMs / value.count).toFixed(2)),
maxMs: Number(value.maxMs.toFixed(2)),
},
]),
);
}
setInterval(() => {
if (requestMetrics.size === 0 && dbMetrics.size === 0) return;
console.log(JSON.stringify({
type: "database_performance",
type: "performance_summary",
timestamp: new Date().toISOString(),
operations,
requests: summarize([...requestMetrics.entries()]),
database: summarize([...dbMetrics.entries()]),
eventLoopDelayMs: {
mean: Number((eventLoop.mean / 1e6 || 0).toFixed(2)),
p95: Number((eventLoop.percentile(95) / 1e6).toFixed(2)),
max: Number((eventLoop.max / 1e6).toFixed(2)),
},
}));
metrics.clear();
requestMetrics.clear();
dbMetrics.clear();
eventLoop.reset();
}, 10_000).unref();