nestworker 2.0.8 → 2.1.1
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/README.md +388 -117
- package/dist/core/worker.interfaces.d.ts +89 -10
- package/dist/core/worker.module.d.ts +21 -11
- package/dist/core/worker.module.js +38 -10
- package/dist/core/worker.module.js.map +1 -1
- package/dist/core/worker.pool.d.ts +26 -6
- package/dist/core/worker.pool.js +288 -103
- package/dist/core/worker.pool.js.map +1 -1
- package/dist/core/worker.service.d.ts +28 -74
- package/dist/core/worker.service.js +132 -79
- package/dist/core/worker.service.js.map +1 -1
- package/dist/decorators/worker-task.decorator.d.ts +7 -25
- package/dist/decorators/worker-task.decorator.js +5 -26
- package/dist/decorators/worker-task.decorator.js.map +1 -1
- package/dist/discovery/discovery.service.d.ts +2 -9
- package/dist/discovery/discovery.service.js +86 -21
- package/dist/discovery/discovery.service.js.map +1 -1
- package/dist/example/bench.d.ts +13 -0
- package/dist/example/bench.js +85 -0
- package/dist/example/bench.js.map +1 -0
- package/dist/example/main.js +14 -2
- package/dist/example/main.js.map +1 -1
- package/dist/health/worker.health.d.ts +46 -0
- package/dist/health/worker.health.js +77 -0
- package/dist/health/worker.health.js.map +1 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/metrics/worker.metrics.d.ts +65 -0
- package/dist/metrics/worker.metrics.js +122 -0
- package/dist/metrics/worker.metrics.js.map +1 -0
- package/dist/worker/worker-runtime.js +139 -29
- package/dist/worker/worker-runtime.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
/**
|
|
13
|
+
* Throughput / latency micro-benchmark.
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* npx ts-node src/example/bench.ts # default 5000 tasks
|
|
17
|
+
* TASKS=20000 POOL=8 npx ts-node src/example/bench.ts
|
|
18
|
+
*
|
|
19
|
+
* Reports:
|
|
20
|
+
* - cold-start time (until pool is ready for first task)
|
|
21
|
+
* - p50 / p95 / p99 / max round-trip latency
|
|
22
|
+
* - sustained throughput (tasks / second)
|
|
23
|
+
*/
|
|
24
|
+
require("reflect-metadata");
|
|
25
|
+
const core_1 = require("@nestjs/core");
|
|
26
|
+
const common_1 = require("@nestjs/common");
|
|
27
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
28
|
+
const node_perf_hooks_1 = require("node:perf_hooks");
|
|
29
|
+
const worker_module_1 = require("../core/worker.module");
|
|
30
|
+
const worker_service_1 = require("../core/worker.service");
|
|
31
|
+
const config_service_1 = require("./config.service");
|
|
32
|
+
const image_service_1 = require("./image.service");
|
|
33
|
+
const TASKS = Number(process.env.TASKS ?? 5_000);
|
|
34
|
+
const POOL = Number(process.env.POOL ?? node_os_1.default.cpus().length);
|
|
35
|
+
const WARMUP = Number(process.env.WARMUP ?? 200);
|
|
36
|
+
let BenchModule = class BenchModule {
|
|
37
|
+
};
|
|
38
|
+
BenchModule = __decorate([
|
|
39
|
+
(0, common_1.Module)({
|
|
40
|
+
imports: [worker_module_1.WorkerModule.forRoot({ poolSize: POOL, shutdownTimeout: 5_000 })],
|
|
41
|
+
providers: [config_service_1.ConfigService, image_service_1.ImageService],
|
|
42
|
+
})
|
|
43
|
+
], BenchModule);
|
|
44
|
+
function pct(sorted, p) {
|
|
45
|
+
if (sorted.length === 0)
|
|
46
|
+
return 0;
|
|
47
|
+
const idx = Math.min(sorted.length - 1, Math.floor((p / 100) * sorted.length));
|
|
48
|
+
return sorted[idx];
|
|
49
|
+
}
|
|
50
|
+
async function run() {
|
|
51
|
+
console.log(`▶ bench: tasks=${TASKS} pool=${POOL} warmup=${WARMUP}`);
|
|
52
|
+
const t0 = node_perf_hooks_1.performance.now();
|
|
53
|
+
const app = await core_1.NestFactory.createApplicationContext(BenchModule, { logger: false });
|
|
54
|
+
const ws = app.get(worker_service_1.WorkerService);
|
|
55
|
+
// Cold-start: first task forces pool readiness.
|
|
56
|
+
await ws.run('ImageService', 'moduleRequire');
|
|
57
|
+
const cold = node_perf_hooks_1.performance.now() - t0;
|
|
58
|
+
console.log(` cold-start: ${cold.toFixed(1)} ms`);
|
|
59
|
+
// Warm-up — JIT, MessagePort priming, etc.
|
|
60
|
+
await Promise.all(Array.from({ length: WARMUP }, () => ws.run('ImageService', 'moduleRequire')));
|
|
61
|
+
// Measured run — uses the cheapest task (cached require) so we measure
|
|
62
|
+
// pool + IPC overhead rather than CPU work.
|
|
63
|
+
const latencies = new Float64Array(TASKS);
|
|
64
|
+
const start = node_perf_hooks_1.performance.now();
|
|
65
|
+
await Promise.all(Array.from({ length: TASKS }, (_, i) => {
|
|
66
|
+
const t = node_perf_hooks_1.performance.now();
|
|
67
|
+
return ws.run('ImageService', 'moduleRequire').then(() => {
|
|
68
|
+
latencies[i] = node_perf_hooks_1.performance.now() - t;
|
|
69
|
+
});
|
|
70
|
+
}));
|
|
71
|
+
const wall = node_perf_hooks_1.performance.now() - start;
|
|
72
|
+
const sorted = Array.from(latencies).sort((a, b) => a - b);
|
|
73
|
+
console.log(` wall: ${wall.toFixed(1)} ms`);
|
|
74
|
+
console.log(` throughput: ${(TASKS / (wall / 1000)).toFixed(0)} tasks/s`);
|
|
75
|
+
console.log(` p50: ${pct(sorted, 50).toFixed(3)} ms`);
|
|
76
|
+
console.log(` p95: ${pct(sorted, 95).toFixed(3)} ms`);
|
|
77
|
+
console.log(` p99: ${pct(sorted, 99).toFixed(3)} ms`);
|
|
78
|
+
console.log(` max: ${sorted[sorted.length - 1].toFixed(3)} ms`);
|
|
79
|
+
await app.close();
|
|
80
|
+
}
|
|
81
|
+
run().catch((err) => {
|
|
82
|
+
console.error('Bench failed:', err);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
});
|
|
85
|
+
//# sourceMappingURL=bench.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bench.js","sourceRoot":"","sources":["../../src/example/bench.ts"],"names":[],"mappings":"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"}
|
package/dist/example/main.js
CHANGED
|
@@ -17,7 +17,7 @@ let AppModule = class AppModule {
|
|
|
17
17
|
};
|
|
18
18
|
AppModule = __decorate([
|
|
19
19
|
(0, common_1.Module)({
|
|
20
|
-
imports: [worker_module_1.WorkerModule.forRoot({ poolSize:
|
|
20
|
+
imports: [worker_module_1.WorkerModule.forRoot({ poolSize: 4, shutdownTimeout: 10000 })],
|
|
21
21
|
providers: [config_service_1.ConfigService, image_service_1.ImageService],
|
|
22
22
|
})
|
|
23
23
|
], AppModule);
|
|
@@ -26,6 +26,18 @@ async function bootstrap() {
|
|
|
26
26
|
logger: false,
|
|
27
27
|
});
|
|
28
28
|
const workerService = app.get(worker_service_1.WorkerService);
|
|
29
|
+
workerService.onTaskStart((task) => {
|
|
30
|
+
console.log('Task started:', task.jobId, workerService.stats());
|
|
31
|
+
});
|
|
32
|
+
workerService.onTaskEnd(((task, durationMs) => {
|
|
33
|
+
console.log('Task ended:', task.jobId, durationMs);
|
|
34
|
+
}));
|
|
35
|
+
workerService.onTaskError(((task, error) => {
|
|
36
|
+
console.log('Task error:', task.jobId, error);
|
|
37
|
+
}));
|
|
38
|
+
workerService.onDead((task) => {
|
|
39
|
+
console.log('Task dead:', task.jobId);
|
|
40
|
+
});
|
|
29
41
|
// ── Task 1: sequential ────────────────────────────────────────────────────
|
|
30
42
|
console.log('▶ resizeImage [priority: HIGH]');
|
|
31
43
|
console.time('resizeImage');
|
|
@@ -35,7 +47,7 @@ async function bootstrap() {
|
|
|
35
47
|
// ── Task 2: sequential ────────────────────────────────────────────────────
|
|
36
48
|
console.log('\n▶ generateThumbnail [priority: LOW override]');
|
|
37
49
|
console.time('generateThumbnail');
|
|
38
|
-
const thumb = await workerService.run('ImageService', 'generateThumbnail', [1920, 1080], { priority: 'LOW' });
|
|
50
|
+
const thumb = await workerService.run('ImageService', 'generateThumbnail', [1920, 1080], { priority: 'LOW', retry: 3, retryDelay: 1000 });
|
|
39
51
|
console.timeEnd('generateThumbnail');
|
|
40
52
|
console.log(' result:', thumb);
|
|
41
53
|
// ── Task 3: concurrent — proves multiple workers run in parallel ───────────
|
package/dist/example/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/example/main.ts"],"names":[],"mappings":";;;;;;;;AAAA,4BAA0B;AAC1B,
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/example/main.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { WorkerService } from '../core/worker.service';
|
|
2
|
+
export interface WorkerHealthResult {
|
|
3
|
+
status: 'up' | 'down';
|
|
4
|
+
details: {
|
|
5
|
+
poolSize: number;
|
|
6
|
+
idle: number;
|
|
7
|
+
busy: number;
|
|
8
|
+
queued: number;
|
|
9
|
+
warmingUp: number;
|
|
10
|
+
};
|
|
11
|
+
error?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* WorkerHealthIndicator — plugs into NestJS Terminus.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* // health.controller.ts
|
|
18
|
+
* import { HealthCheck, HealthCheckService } from '@nestjs/terminus';
|
|
19
|
+
* import { WorkerHealthIndicator } from 'nestworker';
|
|
20
|
+
*
|
|
21
|
+
* @Controller('health')
|
|
22
|
+
* export class HealthController {
|
|
23
|
+
* constructor(
|
|
24
|
+
* private health: HealthCheckService,
|
|
25
|
+
* private workerHealth: WorkerHealthIndicator,
|
|
26
|
+
* ) {}
|
|
27
|
+
*
|
|
28
|
+
* @Get()
|
|
29
|
+
* @HealthCheck()
|
|
30
|
+
* check() {
|
|
31
|
+
* return this.health.check([
|
|
32
|
+
* () => this.workerHealth.check('workers'),
|
|
33
|
+
* ]);
|
|
34
|
+
* }
|
|
35
|
+
* }
|
|
36
|
+
*/
|
|
37
|
+
export declare class WorkerHealthIndicator {
|
|
38
|
+
private readonly workerService;
|
|
39
|
+
constructor(workerService: WorkerService);
|
|
40
|
+
/**
|
|
41
|
+
* Returns a Terminus-compatible health indicator result.
|
|
42
|
+
* Reports 'down' when warmingUp > 0 (pool not fully ready) or when
|
|
43
|
+
* queued jobs exceed the pool size (backpressure signal).
|
|
44
|
+
*/
|
|
45
|
+
check(key: string): Record<string, WorkerHealthResult>;
|
|
46
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.WorkerHealthIndicator = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const worker_service_1 = require("../core/worker.service");
|
|
15
|
+
/**
|
|
16
|
+
* WorkerHealthIndicator — plugs into NestJS Terminus.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* // health.controller.ts
|
|
20
|
+
* import { HealthCheck, HealthCheckService } from '@nestjs/terminus';
|
|
21
|
+
* import { WorkerHealthIndicator } from 'nestworker';
|
|
22
|
+
*
|
|
23
|
+
* @Controller('health')
|
|
24
|
+
* export class HealthController {
|
|
25
|
+
* constructor(
|
|
26
|
+
* private health: HealthCheckService,
|
|
27
|
+
* private workerHealth: WorkerHealthIndicator,
|
|
28
|
+
* ) {}
|
|
29
|
+
*
|
|
30
|
+
* @Get()
|
|
31
|
+
* @HealthCheck()
|
|
32
|
+
* check() {
|
|
33
|
+
* return this.health.check([
|
|
34
|
+
* () => this.workerHealth.check('workers'),
|
|
35
|
+
* ]);
|
|
36
|
+
* }
|
|
37
|
+
* }
|
|
38
|
+
*/
|
|
39
|
+
let WorkerHealthIndicator = class WorkerHealthIndicator {
|
|
40
|
+
workerService;
|
|
41
|
+
constructor(workerService) {
|
|
42
|
+
this.workerService = workerService;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Returns a Terminus-compatible health indicator result.
|
|
46
|
+
* Reports 'down' when warmingUp > 0 (pool not fully ready) or when
|
|
47
|
+
* queued jobs exceed the pool size (backpressure signal).
|
|
48
|
+
*/
|
|
49
|
+
check(key) {
|
|
50
|
+
const stats = this.workerService.stats();
|
|
51
|
+
const isDown = stats.warmingUp > 0 ||
|
|
52
|
+
stats.queued > stats.poolSize;
|
|
53
|
+
const result = {
|
|
54
|
+
status: isDown ? 'down' : 'up',
|
|
55
|
+
details: stats,
|
|
56
|
+
...(isDown && stats.warmingUp > 0
|
|
57
|
+
? { error: `${stats.warmingUp} worker(s) still warming up` }
|
|
58
|
+
: {}),
|
|
59
|
+
...(isDown && stats.queued > stats.poolSize
|
|
60
|
+
? { error: `Queue depth (${stats.queued}) exceeds pool size (${stats.poolSize})` }
|
|
61
|
+
: {}),
|
|
62
|
+
};
|
|
63
|
+
if (isDown) {
|
|
64
|
+
throw Object.assign(new Error(`Worker pool unhealthy`), {
|
|
65
|
+
[key]: result,
|
|
66
|
+
causes: [result.error],
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return { [key]: result };
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
exports.WorkerHealthIndicator = WorkerHealthIndicator;
|
|
73
|
+
exports.WorkerHealthIndicator = WorkerHealthIndicator = __decorate([
|
|
74
|
+
(0, common_1.Injectable)(),
|
|
75
|
+
__metadata("design:paramtypes", [worker_service_1.WorkerService])
|
|
76
|
+
], WorkerHealthIndicator);
|
|
77
|
+
//# sourceMappingURL=worker.health.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.health.js","sourceRoot":"","sources":["../../src/health/worker.health.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,2DAAuD;AAcvD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEI,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IACH;IAA7B,YAA6B,aAA4B;QAA5B,kBAAa,GAAb,aAAa,CAAe;IAAG,CAAC;IAE7D;;;;OAIG;IACH,KAAK,CAAC,GAAW;QACf,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;QACzC,MAAM,MAAM,GACV,KAAK,CAAC,SAAS,GAAG,CAAC;YACnB,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,QAAQ,CAAC;QAEhC,MAAM,MAAM,GAAuB;YACjC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI;YAC9B,OAAO,EAAE,KAAK;YACd,GAAG,CAAC,MAAM,IAAI,KAAK,CAAC,SAAS,GAAG,CAAC;gBAC/B,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC,SAAS,6BAA6B,EAAE;gBAC5D,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,QAAQ;gBACzC,CAAC,CAAC,EAAE,KAAK,EAAE,gBAAgB,KAAK,CAAC,MAAM,wBAAwB,KAAK,CAAC,QAAQ,GAAG,EAAE;gBAClF,CAAC,CAAC,EAAE,CAAC;SACR,CAAC;QAEF,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,uBAAuB,CAAC,EAAE;gBACtD,CAAC,GAAG,CAAC,EAAE,MAAM;gBACb,MAAM,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC;aACvB,CAAC,CAAC;QACL,CAAC;QAED,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,CAAC;CACF,CAAA;AAlCY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,mBAAU,GAAE;qCAEiC,8BAAa;GAD9C,qBAAqB,CAkCjC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
export { WorkerModule } from './core/worker.module';
|
|
2
2
|
export { WorkerService } from './core/worker.service';
|
|
3
|
+
export type { RunOptions } from './core/worker.service';
|
|
3
4
|
export { WorkerClass, WorkerTask } from './decorators/worker-task.decorator';
|
|
4
|
-
export type { WorkerModuleOptions, TaskPriority } from './core/worker.interfaces';
|
|
5
5
|
export type { WorkerTaskOptions } from './decorators/worker-task.decorator';
|
|
6
|
+
export { WorkerHealthIndicator } from './health/worker.health';
|
|
7
|
+
export type { WorkerHealthResult } from './health/worker.health';
|
|
8
|
+
export { WorkerMetricsService } from './metrics/worker.metrics';
|
|
9
|
+
export type { WorkerMetricsSnapshot } from './metrics/worker.metrics';
|
|
10
|
+
export type { WorkerJob, WorkerModuleOptions, WorkerModuleAsyncOptions, TaskPriority, DeadLetterEvent, PoolStats, SerializedError, } from './core/worker.interfaces';
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.WorkerTask = exports.WorkerClass = exports.WorkerService = exports.WorkerModule = void 0;
|
|
3
|
+
exports.WorkerMetricsService = exports.WorkerHealthIndicator = exports.WorkerTask = exports.WorkerClass = exports.WorkerService = exports.WorkerModule = void 0;
|
|
4
|
+
// Module
|
|
4
5
|
var worker_module_1 = require("./core/worker.module");
|
|
5
6
|
Object.defineProperty(exports, "WorkerModule", { enumerable: true, get: function () { return worker_module_1.WorkerModule; } });
|
|
7
|
+
// Services
|
|
6
8
|
var worker_service_1 = require("./core/worker.service");
|
|
7
9
|
Object.defineProperty(exports, "WorkerService", { enumerable: true, get: function () { return worker_service_1.WorkerService; } });
|
|
10
|
+
// Decorators
|
|
8
11
|
var worker_task_decorator_1 = require("./decorators/worker-task.decorator");
|
|
9
12
|
Object.defineProperty(exports, "WorkerClass", { enumerable: true, get: function () { return worker_task_decorator_1.WorkerClass; } });
|
|
10
13
|
Object.defineProperty(exports, "WorkerTask", { enumerable: true, get: function () { return worker_task_decorator_1.WorkerTask; } });
|
|
14
|
+
// Health
|
|
15
|
+
var worker_health_1 = require("./health/worker.health");
|
|
16
|
+
Object.defineProperty(exports, "WorkerHealthIndicator", { enumerable: true, get: function () { return worker_health_1.WorkerHealthIndicator; } });
|
|
17
|
+
// Metrics
|
|
18
|
+
var worker_metrics_1 = require("./metrics/worker.metrics");
|
|
19
|
+
Object.defineProperty(exports, "WorkerMetricsService", { enumerable: true, get: function () { return worker_metrics_1.WorkerMetricsService; } });
|
|
11
20
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,SAAS;AACT,sDAAsD;AAA7C,6GAAA,YAAY,OAAA;AAErB,WAAW;AACX,wDAAuD;AAA9C,+GAAA,aAAa,OAAA;AAGtB,aAAa;AACb,4EAA6E;AAApE,oHAAA,WAAW,OAAA;AAAE,mHAAA,UAAU,OAAA;AAGhC,SAAS;AACT,wDAA+D;AAAtD,sHAAA,qBAAqB,OAAA;AAG9B,UAAU;AACV,2DAAiE;AAAxD,sHAAA,oBAAoB,OAAA"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { OnModuleInit, OnModuleDestroy } from '@nestjs/common';
|
|
2
|
+
import { WorkerService } from '../core/worker.service';
|
|
3
|
+
export interface WorkerMetricsSnapshot {
|
|
4
|
+
/** Total jobs dispatched since startup */
|
|
5
|
+
jobsTotal: number;
|
|
6
|
+
/** Jobs that completed successfully */
|
|
7
|
+
jobsSuccess: number;
|
|
8
|
+
/** Jobs that failed (after all retries) */
|
|
9
|
+
jobsFailed: number;
|
|
10
|
+
/** Jobs that timed out */
|
|
11
|
+
jobsTimeout: number;
|
|
12
|
+
/** Jobs sent to dead letter queue */
|
|
13
|
+
jobsDead: number;
|
|
14
|
+
/** Current queue depth */
|
|
15
|
+
queueDepth: number;
|
|
16
|
+
/** Current idle worker count */
|
|
17
|
+
idleWorkers: number;
|
|
18
|
+
/** Current busy worker count */
|
|
19
|
+
busyWorkers: number;
|
|
20
|
+
/** Per-task duration histogram (p50, p95, p99) in ms */
|
|
21
|
+
durations: Record<string, {
|
|
22
|
+
p50: number;
|
|
23
|
+
p95: number;
|
|
24
|
+
p99: number;
|
|
25
|
+
count: number;
|
|
26
|
+
}>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* WorkerMetricsService — collects runtime metrics from the worker pool.
|
|
30
|
+
*
|
|
31
|
+
* Designed to be framework-agnostic: read the snapshot and push to
|
|
32
|
+
* Prometheus, Datadog, CloudWatch, or any other metrics provider.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // Prometheus integration
|
|
36
|
+
* @Get('metrics')
|
|
37
|
+
* async metrics() {
|
|
38
|
+
* const snap = this.workerMetrics.snapshot();
|
|
39
|
+
* return [
|
|
40
|
+
* `nestworker_jobs_total ${snap.jobsTotal}`,
|
|
41
|
+
* `nestworker_jobs_success ${snap.jobsSuccess}`,
|
|
42
|
+
* `nestworker_queue_depth ${snap.queueDepth}`,
|
|
43
|
+
* ].join('\n');
|
|
44
|
+
* }
|
|
45
|
+
*/
|
|
46
|
+
export declare class WorkerMetricsService implements OnModuleInit, OnModuleDestroy {
|
|
47
|
+
private readonly workerService;
|
|
48
|
+
private jobsTotal;
|
|
49
|
+
private jobsSuccess;
|
|
50
|
+
private jobsFailed;
|
|
51
|
+
private jobsTimeout;
|
|
52
|
+
private jobsDead;
|
|
53
|
+
/** Raw duration samples per task key — capped at 1000 samples */
|
|
54
|
+
private readonly durationSamples;
|
|
55
|
+
/** Interval handle for periodic pool-stats polling */
|
|
56
|
+
private statsInterval?;
|
|
57
|
+
constructor(workerService: WorkerService);
|
|
58
|
+
onModuleInit(): void;
|
|
59
|
+
onModuleDestroy(): void;
|
|
60
|
+
/** Returns a point-in-time snapshot of all metrics */
|
|
61
|
+
snapshot(): WorkerMetricsSnapshot;
|
|
62
|
+
/** Reset all counters (useful in tests) */
|
|
63
|
+
reset(): void;
|
|
64
|
+
private pushDuration;
|
|
65
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.WorkerMetricsService = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const worker_service_1 = require("../core/worker.service");
|
|
15
|
+
/**
|
|
16
|
+
* WorkerMetricsService — collects runtime metrics from the worker pool.
|
|
17
|
+
*
|
|
18
|
+
* Designed to be framework-agnostic: read the snapshot and push to
|
|
19
|
+
* Prometheus, Datadog, CloudWatch, or any other metrics provider.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* // Prometheus integration
|
|
23
|
+
* @Get('metrics')
|
|
24
|
+
* async metrics() {
|
|
25
|
+
* const snap = this.workerMetrics.snapshot();
|
|
26
|
+
* return [
|
|
27
|
+
* `nestworker_jobs_total ${snap.jobsTotal}`,
|
|
28
|
+
* `nestworker_jobs_success ${snap.jobsSuccess}`,
|
|
29
|
+
* `nestworker_queue_depth ${snap.queueDepth}`,
|
|
30
|
+
* ].join('\n');
|
|
31
|
+
* }
|
|
32
|
+
*/
|
|
33
|
+
let WorkerMetricsService = class WorkerMetricsService {
|
|
34
|
+
workerService;
|
|
35
|
+
jobsTotal = 0;
|
|
36
|
+
jobsSuccess = 0;
|
|
37
|
+
jobsFailed = 0;
|
|
38
|
+
jobsTimeout = 0;
|
|
39
|
+
jobsDead = 0;
|
|
40
|
+
/** Raw duration samples per task key — capped at 1000 samples */
|
|
41
|
+
durationSamples = new Map();
|
|
42
|
+
/** Interval handle for periodic pool-stats polling */
|
|
43
|
+
statsInterval;
|
|
44
|
+
constructor(workerService) {
|
|
45
|
+
this.workerService = workerService;
|
|
46
|
+
}
|
|
47
|
+
onModuleInit() {
|
|
48
|
+
// Task start
|
|
49
|
+
this.workerService.onTaskStart((job) => {
|
|
50
|
+
this.jobsTotal++;
|
|
51
|
+
});
|
|
52
|
+
// Task end: record duration + success
|
|
53
|
+
this.workerService.onTaskEnd((job, durationMs) => {
|
|
54
|
+
const key = `${job.serviceName}.${job.methodName}`;
|
|
55
|
+
this.jobsSuccess++;
|
|
56
|
+
this.pushDuration(key, durationMs);
|
|
57
|
+
});
|
|
58
|
+
// Task error: record failure
|
|
59
|
+
this.workerService.onTaskError((job, error) => {
|
|
60
|
+
this.jobsFailed++;
|
|
61
|
+
if (error.name === 'TimeoutError')
|
|
62
|
+
this.jobsTimeout++;
|
|
63
|
+
});
|
|
64
|
+
// Dead letter: separate counter
|
|
65
|
+
this.workerService.onDead(() => {
|
|
66
|
+
this.jobsDead++;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
onModuleDestroy() {
|
|
70
|
+
if (this.statsInterval)
|
|
71
|
+
clearInterval(this.statsInterval);
|
|
72
|
+
}
|
|
73
|
+
/** Returns a point-in-time snapshot of all metrics */
|
|
74
|
+
snapshot() {
|
|
75
|
+
const stats = this.workerService.stats();
|
|
76
|
+
const durations = {};
|
|
77
|
+
for (const [key, samples] of this.durationSamples) {
|
|
78
|
+
durations[key] = computePercentiles(samples);
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
jobsTotal: this.jobsTotal,
|
|
82
|
+
jobsSuccess: this.jobsSuccess,
|
|
83
|
+
jobsFailed: this.jobsFailed,
|
|
84
|
+
jobsTimeout: this.jobsTimeout,
|
|
85
|
+
jobsDead: this.jobsDead,
|
|
86
|
+
queueDepth: stats.queued,
|
|
87
|
+
idleWorkers: stats.idle,
|
|
88
|
+
busyWorkers: stats.busy,
|
|
89
|
+
durations,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/** Reset all counters (useful in tests) */
|
|
93
|
+
reset() {
|
|
94
|
+
this.jobsTotal = this.jobsSuccess = this.jobsFailed =
|
|
95
|
+
this.jobsTimeout = this.jobsDead = 0;
|
|
96
|
+
this.durationSamples.clear();
|
|
97
|
+
}
|
|
98
|
+
pushDuration(key, ms) {
|
|
99
|
+
if (!this.durationSamples.has(key))
|
|
100
|
+
this.durationSamples.set(key, []);
|
|
101
|
+
const samples = this.durationSamples.get(key);
|
|
102
|
+
samples.push(ms);
|
|
103
|
+
// Keep memory bounded — reservoir sampling after 1000 entries
|
|
104
|
+
if (samples.length > 1000) {
|
|
105
|
+
const idx = Math.floor(Math.random() * samples.length);
|
|
106
|
+
samples.splice(idx, 1);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
exports.WorkerMetricsService = WorkerMetricsService;
|
|
111
|
+
exports.WorkerMetricsService = WorkerMetricsService = __decorate([
|
|
112
|
+
(0, common_1.Injectable)(),
|
|
113
|
+
__metadata("design:paramtypes", [worker_service_1.WorkerService])
|
|
114
|
+
], WorkerMetricsService);
|
|
115
|
+
function computePercentiles(samples) {
|
|
116
|
+
if (samples.length === 0)
|
|
117
|
+
return { p50: 0, p95: 0, p99: 0, count: 0 };
|
|
118
|
+
const sorted = [...samples].sort((a, b) => a - b);
|
|
119
|
+
const p = (pct) => sorted[Math.ceil((pct / 100) * sorted.length) - 1] ?? 0;
|
|
120
|
+
return { p50: p(50), p95: p(95), p99: p(99), count: samples.length };
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=worker.metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.metrics.js","sourceRoot":"","sources":["../../src/metrics/worker.metrics.ts"],"names":[],"mappings":"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"}
|