mirror of
https://github.com/wekan/wekan.git
synced 2026-11-07 02:52:33 -05:00
WeKan and FerretDB on one machine can drive CPU high enough to starve other software. Add a monitor, a governor, and a report. Design: docs/Features/CPU/CPU.md. Monitor (server/lib/cpuMonitor.js): samples system-wide CPU% from os.cpus() idle/total deltas on an interval (so it includes WeKan, FerretDB and every other process). A pure, hysteresis state machine (models/lib/cpuHighTracker.js) turns the samples into ONLY the START and END of each sustained high-CPU period — two rows per episode, never a flood. Enters high after 3 consecutive samples >= 85%, leaves after 3 consecutive < 70%. Each row carries duration, peak, system load, and what WeKan was doing (setActivity label). Recorded to a new 'cpu' event stream (server/lib/cpuLog.js). Governor: cpuMonitor exports isHighCpu() and pauseIfBusy(), so long batch operations await a short pause and yield the CPU only while the system is busy (no cost when idle). Wired into the existing-file extension corrector. Tunable via WEKAN_CPU_* env vars. Report: 'cpu' added to EVENT_STREAMS; reuses the generic eventStreamReport (stream="cpu") with search + pagination; a "CPU usage" item is added to the Admin Panel / Problems side menu (cpuReportTitle i18n). Tests: cpuHighTracker unit (start/end, duration/peak, no-flap hysteresis; positive + negative) and cpuMonitorWiring source guards. CHANGELOG + design doc.
34 lines
1.4 KiB
JavaScript
34 lines
1.4 KiB
JavaScript
// ============================================================================
|
|
// WeKan CPU-usage event logger (Admin Panel → Problems → CPU usage).
|
|
// ----------------------------------------------------------------------------
|
|
// Records ONLY the START and END of each sustained high-CPU period (not a
|
|
// continuous stream) as documents in the existing WeKan database
|
|
// (models/eventLog.js, stream:'cpu') via a normal Meteor JavaScript query — so it
|
|
// works identically on FerretDB and MongoDB. Best-effort, never throws.
|
|
// ============================================================================
|
|
|
|
import EventLog from '/models/eventLog';
|
|
const { sanitizeDetail } = require('/models/lib/securityLogFormat');
|
|
|
|
export function record(evt = {}) {
|
|
try {
|
|
const doc = {
|
|
stream: 'cpu',
|
|
at: evt.at instanceof Date ? evt.at : new Date(),
|
|
severity: evt.severity || 'medium',
|
|
category: evt.category || 'cpu',
|
|
bleed: evt.bleed || 'CpuBleed',
|
|
action: evt.action || 'detected',
|
|
source: evt.source || 'cpuMonitor',
|
|
cwe: evt.cwe || 'CWE-400',
|
|
detail: sanitizeDetail(evt.detail),
|
|
};
|
|
const p = EventLog.insertAsync(doc);
|
|
if (p && typeof p.catch === 'function') p.catch(() => {});
|
|
} catch (e) {
|
|
if (process.env.DEBUG === 'true') console.warn('cpuLog.record failed:', e && e.message);
|
|
}
|
|
}
|
|
|
|
export default { record };
|