@maioradv/nestjs-core 2.0.2 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/guards/index.d.ts
CHANGED
package/dist/guards/index.js
CHANGED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { LoggerService } from '@nestjs/common';
|
|
2
|
+
export type ResourceGuardOptions = {
|
|
3
|
+
/** CPU limit in percent (per core). Optional. */
|
|
4
|
+
cpuLimit?: number;
|
|
5
|
+
/** Memory limit in MB (RSS). Optional. */
|
|
6
|
+
memoryLimitMb?: number;
|
|
7
|
+
/** Interval in ms to check CPU/memory. Default: 2000 */
|
|
8
|
+
sampleIntervalMs?: number;
|
|
9
|
+
/** How long in ms the limit must be exceeded before shutdown. Default: 10000 */
|
|
10
|
+
sustainForMs?: number;
|
|
11
|
+
/** Maximum time in ms to wait for shutdownFn before hard kill. Default: 5000 */
|
|
12
|
+
hardKillAfterMs?: number;
|
|
13
|
+
/** Optional async shutdown function. If provided, will be called before hard exit. */
|
|
14
|
+
shutdownFn?: () => Promise<void>;
|
|
15
|
+
/** Optional NestJS LoggerService to use for logging. */
|
|
16
|
+
logger?: LoggerService;
|
|
17
|
+
};
|
|
18
|
+
export declare class ResourceGuard {
|
|
19
|
+
private readonly opts;
|
|
20
|
+
private lastCpu;
|
|
21
|
+
private lastTime;
|
|
22
|
+
private overSince;
|
|
23
|
+
private shuttingDown;
|
|
24
|
+
constructor(opts: ResourceGuardOptions);
|
|
25
|
+
/** Start monitoring CPU and memory */
|
|
26
|
+
start(): void;
|
|
27
|
+
private check;
|
|
28
|
+
private shutdown;
|
|
29
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ResourceGuard = void 0;
|
|
4
|
+
class ResourceGuard {
|
|
5
|
+
constructor(opts) {
|
|
6
|
+
this.opts = opts;
|
|
7
|
+
this.lastCpu = process.cpuUsage();
|
|
8
|
+
this.lastTime = Date.now();
|
|
9
|
+
this.overSince = null;
|
|
10
|
+
this.shuttingDown = false;
|
|
11
|
+
this.opts.sampleIntervalMs ??= 2000;
|
|
12
|
+
this.opts.sustainForMs ??= 10000;
|
|
13
|
+
this.opts.hardKillAfterMs ??= 5000;
|
|
14
|
+
}
|
|
15
|
+
/** Start monitoring CPU and memory */
|
|
16
|
+
start() {
|
|
17
|
+
if (!this.opts.cpuLimit && !this.opts.memoryLimitMb)
|
|
18
|
+
return;
|
|
19
|
+
setInterval(() => this.check(), this.opts.sampleIntervalMs).unref();
|
|
20
|
+
}
|
|
21
|
+
async check() {
|
|
22
|
+
if (this.shuttingDown)
|
|
23
|
+
return;
|
|
24
|
+
const nowTime = Date.now();
|
|
25
|
+
let over = false;
|
|
26
|
+
let currentCpuPercent = null;
|
|
27
|
+
let currentMemMb = null;
|
|
28
|
+
if (this.opts.cpuLimit) {
|
|
29
|
+
const now = process.cpuUsage(this.lastCpu);
|
|
30
|
+
const elapsedMs = nowTime - this.lastTime;
|
|
31
|
+
const cpuMs = (now.user + now.system) / 1000;
|
|
32
|
+
currentCpuPercent = (cpuMs / elapsedMs) * 100;
|
|
33
|
+
if (currentCpuPercent > this.opts.cpuLimit)
|
|
34
|
+
over = true;
|
|
35
|
+
this.lastCpu = process.cpuUsage();
|
|
36
|
+
}
|
|
37
|
+
if (this.opts.memoryLimitMb) {
|
|
38
|
+
currentMemMb = process.memoryUsage().rss / 1024 / 1024;
|
|
39
|
+
if (currentMemMb > this.opts.memoryLimitMb)
|
|
40
|
+
over = true;
|
|
41
|
+
}
|
|
42
|
+
this.lastTime = nowTime;
|
|
43
|
+
if (over) {
|
|
44
|
+
if (!this.overSince)
|
|
45
|
+
this.overSince = nowTime;
|
|
46
|
+
if (nowTime - this.overSince >= this.opts.sustainForMs) {
|
|
47
|
+
await this.shutdown(currentCpuPercent, currentMemMb);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
this.overSince = null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async shutdown(currentCpu, currentMem) {
|
|
55
|
+
if (this.shuttingDown)
|
|
56
|
+
return;
|
|
57
|
+
this.shuttingDown = true;
|
|
58
|
+
const log = (msg) => {
|
|
59
|
+
if (this.opts.logger)
|
|
60
|
+
this.opts.logger.error(`${msg}`);
|
|
61
|
+
else
|
|
62
|
+
console.error(`${msg}`);
|
|
63
|
+
};
|
|
64
|
+
const overTimeMs = this.overSince ? Date.now() - this.overSince : 0;
|
|
65
|
+
const cpuMsg = this.opts.cpuLimit ? ` CPU: ${currentCpu?.toFixed(1)}% (${this.opts.cpuLimit}%)` : '';
|
|
66
|
+
const memMsg = this.opts.memoryLimitMb ? ` Memory: ${currentMem?.toFixed(0)}MB (${this.opts.memoryLimitMb}MB)` : '';
|
|
67
|
+
const append = [cpuMsg, memMsg, ` Exceeded for: ${overTimeMs}ms (${this.opts.sustainForMs}ms)`].filter(Boolean).join(';');
|
|
68
|
+
log(`LIMIT EXCEEDED → initiating shutdown;${append}`);
|
|
69
|
+
const hardKill = setTimeout(() => {
|
|
70
|
+
log('HARD EXIT');
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}, this.opts.hardKillAfterMs);
|
|
73
|
+
try {
|
|
74
|
+
if (this.opts.shutdownFn) {
|
|
75
|
+
await this.opts.shutdownFn();
|
|
76
|
+
log('Soft shutdown completed successfully');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch (e) {
|
|
80
|
+
log(`shutdownFn failed: ${e.message}`);
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
clearTimeout(hardKill);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
exports.ResourceGuard = ResourceGuard;
|