UNPKG

redis-smq-common

Version:

Provides essential components and utilities shared across RedisSMQ packages.

159 lines 5.37 kB
import * as crypto from 'crypto'; import os from 'node:os'; export class CPUMonitor { static instance = null; baselines = new Map(); maxBaselineAgeMs; precision; constructor(options = {}) { this.maxBaselineAgeMs = options.maxBaselineAgeMs ?? 300_000; this.precision = Math.max(0, Math.min(3, options.precision ?? 2)); } static getInstance(options = {}) { if (!CPUMonitor.instance) { CPUMonitor.instance = new CPUMonitor(options); } return CPUMonitor.instance; } cleanupOldBaselines() { const now = Date.now(); const cutoff = now - this.maxBaselineAgeMs; for (const [callerId, entry] of this.baselines) { if (entry.timestamp < cutoff) { this.baselines.delete(callerId); } } } takeSnapshot() { const cpus = os.cpus(); if (!cpus.length) { throw new Error('No CPU information available'); } let totalUser = 0; let totalSystem = 0; let totalIdle = 0; let totalAll = 0; cpus.forEach((cpu) => { totalUser += cpu.times.user; totalSystem += cpu.times.sys; totalIdle += cpu.times.idle; totalAll += cpu.times.user + cpu.times.nice + cpu.times.sys + cpu.times.idle + cpu.times.irq; }); return { user: totalUser, system: totalSystem, idle: totalIdle, total: totalAll, timestamp: Date.now(), }; } calculateUsage(start, end) { if (end.timestamp <= start.timestamp) { return { user: 0, system: 0, percentage: '0%' }; } const userDiff = Math.max(0, end.user - start.user); const systemDiff = Math.max(0, end.system - start.system); const totalDiff = Math.max(0, end.total - start.total); if (totalDiff === 0) { return { user: 0, system: 0, percentage: '0%' }; } const userPct = (userDiff / totalDiff) * 100; const systemPct = (systemDiff / totalDiff) * 100; const totalPct = Math.min(100, userPct + systemPct); const factor = 10 ** this.precision; return { user: Math.round(userPct * factor) / factor, system: Math.round(systemPct * factor) / factor, percentage: Math.round(totalPct) + '%', }; } getStats(callerId = 'default') { this.cleanupOldBaselines(); const current = this.takeSnapshot(); const previous = this.baselines.get(callerId); if (!previous) { this.baselines.set(callerId, { snapshot: current, timestamp: Date.now(), }); return { user: 0, system: 0, percentage: '0%' }; } const usage = this.calculateUsage(previous.snapshot, current); this.baselines.set(callerId, { snapshot: current, timestamp: Date.now() }); return usage; } async getStatsOverInterval(callerId = 'default', intervalMs = 1000) { this.cleanupOldBaselines(); const start = this.takeSnapshot(); const tempId = `temp-${callerId}-${Date.now()}`; this.baselines.set(tempId, { snapshot: start, timestamp: Date.now() }); await new Promise((resolve) => setTimeout(resolve, Math.max(0, intervalMs))); const end = this.takeSnapshot(); const usage = this.calculateUsage(start, end); this.baselines.delete(tempId); return usage; } generateCallerId() { return crypto.randomBytes(16).toString('hex'); } removeCaller(callerId) { return this.baselines.delete(callerId); } hasCaller(callerId) { return this.baselines.has(callerId); } getActiveCallerCount() { this.cleanupOldBaselines(); return this.baselines.size; } getActiveCallers() { this.cleanupOldBaselines(); return Array.from(this.baselines.keys()); } getBaselineAge(callerId) { const entry = this.baselines.get(callerId); return entry ? Date.now() - entry.timestamp : null; } reset() { this.baselines.clear(); } cleanup() { this.cleanupOldBaselines(); } getMonitorStats() { this.cleanupOldBaselines(); let oldest = Date.now(); let newest = 0; for (const entry of this.baselines.values()) { oldest = Math.min(oldest, entry.timestamp); newest = Math.max(newest, entry.timestamp); } return { activeCallers: this.baselines.size, oldestBaseline: oldest === Date.now() ? 0 : oldest, newestBaseline: newest, totalBaselines: this.baselines.size, }; } getOneTimeStats() { const id = this.generateCallerId(); const usage = this.getStats(id); this.removeCaller(id); return usage; } getStatsWithAutoId() { const callerId = this.generateCallerId(); const usage = this.getStats(callerId); return { callerId, usage, cleanup: () => this.removeCaller(callerId), }; } } //# sourceMappingURL=cpu-monitor.js.map