nestworker 2.0.8 → 2.1.0
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 +343 -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 +246 -101
- package/dist/core/worker.pool.js.map +1 -1
- package/dist/core/worker.service.d.ts +26 -74
- package/dist/core/worker.service.js +105 -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/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 +124 -27
- package/dist/worker/worker-runtime.js.map +1 -1
- package/package.json +1 -1
|
@@ -11,51 +11,24 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
11
11
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
12
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
13
|
};
|
|
14
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
15
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
16
|
+
};
|
|
17
|
+
var WorkerService_1;
|
|
14
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
19
|
exports.WorkerService = void 0;
|
|
16
20
|
const common_1 = require("@nestjs/common");
|
|
21
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
17
22
|
const worker_pool_1 = require("./worker.pool");
|
|
18
23
|
const discovery_service_1 = require("../discovery/discovery.service");
|
|
19
24
|
const di_serializer_1 = require("../di/di-serializer");
|
|
20
|
-
|
|
21
|
-
* WorkerService
|
|
22
|
-
*
|
|
23
|
-
* Main entry point for executing worker-thread tasks.
|
|
24
|
-
*
|
|
25
|
-
* Features:
|
|
26
|
-
* - Lazy worker pool initialization
|
|
27
|
-
* - Automatic worker discovery
|
|
28
|
-
* - Priority queue support
|
|
29
|
-
* - Timeout support
|
|
30
|
-
* - Worker thread execution
|
|
31
|
-
* - NestJS dependency integration
|
|
32
|
-
*
|
|
33
|
-
* Lifecycle:
|
|
34
|
-
* 1. Discovers worker-enabled services
|
|
35
|
-
* 2. Serializes metadata for worker runtime
|
|
36
|
-
* 3. Creates WorkerPool
|
|
37
|
-
* 4. Dispatches tasks to workers
|
|
38
|
-
* 5. Handles graceful shutdown
|
|
39
|
-
*
|
|
40
|
-
* Example:
|
|
41
|
-
*
|
|
42
|
-
* ```ts
|
|
43
|
-
* await workerService.run(
|
|
44
|
-
* 'ImageService',
|
|
45
|
-
* 'resizeImage',
|
|
46
|
-
* [500],
|
|
47
|
-
* {
|
|
48
|
-
* priority: 'HIGH',
|
|
49
|
-
* timeout: 5000,
|
|
50
|
-
* },
|
|
51
|
-
* );
|
|
52
|
-
* ```
|
|
53
|
-
*/
|
|
54
|
-
let WorkerService = class WorkerService {
|
|
25
|
+
let WorkerService = WorkerService_1 = class WorkerService {
|
|
55
26
|
discovery;
|
|
56
27
|
options;
|
|
28
|
+
logger = new common_1.Logger(WorkerService_1.name);
|
|
57
29
|
pool = null;
|
|
58
|
-
|
|
30
|
+
taskDefaults = new Map();
|
|
31
|
+
taskProxies = new Map();
|
|
59
32
|
constructor(discovery, options) {
|
|
60
33
|
this.discovery = discovery;
|
|
61
34
|
this.options = options;
|
|
@@ -67,69 +40,122 @@ let WorkerService = class WorkerService {
|
|
|
67
40
|
if (this.pool)
|
|
68
41
|
return;
|
|
69
42
|
const tasks = this.discovery.scan();
|
|
43
|
+
const proxyMap = new Map();
|
|
70
44
|
for (const task of tasks) {
|
|
71
|
-
|
|
45
|
+
const key = `${task.serviceName}.${task.methodName}`;
|
|
46
|
+
this.taskDefaults.set(key, {
|
|
47
|
+
priority: task.priority,
|
|
48
|
+
timeout: task.timeout,
|
|
49
|
+
retry: task.retry,
|
|
50
|
+
retryDelay: task.retryDelay,
|
|
51
|
+
});
|
|
52
|
+
const descriptors = task.proxyInstances.map((p) => {
|
|
53
|
+
if (!proxyMap.has(p.propertyKey)) {
|
|
54
|
+
proxyMap.set(p.propertyKey, { methodNames: p.methodNames, instance: p.instance });
|
|
55
|
+
}
|
|
56
|
+
return { propertyKey: p.propertyKey, methodNames: p.methodNames };
|
|
57
|
+
});
|
|
58
|
+
this.taskProxies.set(key, descriptors);
|
|
72
59
|
}
|
|
73
60
|
const serialized = (0, di_serializer_1.serializeForWorker)(tasks);
|
|
74
|
-
|
|
61
|
+
const proxyInstances = Array.from(proxyMap.entries()).map(([propertyKey, { methodNames, instance }]) => ({ propertyKey, methodNames, instance }));
|
|
62
|
+
this.pool = new worker_pool_1.WorkerPool(serialized, proxyInstances, this.options.poolSize, this.options.shutdownTimeout);
|
|
63
|
+
// Forward pool events to logger and expose via EventEmitter
|
|
64
|
+
this.pool.on('dead', (event) => {
|
|
65
|
+
this.logger.error(`Dead letter: ${event.serviceName}.${event.methodName} ` +
|
|
66
|
+
`failed after ${event.attempts} attempt(s) — ${event.error.message}`);
|
|
67
|
+
});
|
|
68
|
+
this.pool.on('error', (err) => {
|
|
69
|
+
this.logger.error(`Worker pool error: ${err.message}`, err.stack);
|
|
70
|
+
});
|
|
75
71
|
}
|
|
76
72
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* Parameters:
|
|
80
|
-
* - serviceName: target service
|
|
81
|
-
* - methodName: target method
|
|
82
|
-
* - args: serialized arguments
|
|
83
|
-
* - overrides: runtime priority/timeout overrides
|
|
73
|
+
* Run a @WorkerTask method in a worker thread.
|
|
84
74
|
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* -
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* Timeout:
|
|
91
|
-
* Automatically terminates timed-out workers.
|
|
92
|
-
*
|
|
93
|
-
* Example:
|
|
94
|
-
*
|
|
95
|
-
* ```ts
|
|
96
|
-
* await workerService.run(
|
|
97
|
-
* 'ImageService',
|
|
98
|
-
* 'generateThumbnail',
|
|
99
|
-
* [1920, 1080],
|
|
100
|
-
* {
|
|
101
|
-
* priority: 'HIGH',
|
|
102
|
-
* timeout: 3000,
|
|
103
|
-
* },
|
|
104
|
-
* );
|
|
105
|
-
* ```
|
|
75
|
+
* @param serviceName Class name of the @WorkerClass provider
|
|
76
|
+
* @param methodName Method decorated with @WorkerTask
|
|
77
|
+
* @param args structuredClone-compatible arguments
|
|
78
|
+
* @param options Optional priority / timeout / retry / AbortSignal overrides
|
|
106
79
|
*/
|
|
107
|
-
run(serviceName, methodName, args = [],
|
|
80
|
+
run(serviceName, methodName, args = [], options = {}) {
|
|
108
81
|
const key = `${serviceName}.${methodName}`;
|
|
109
|
-
const defaults = this.
|
|
110
|
-
|
|
82
|
+
const defaults = this.taskDefaults.get(key) ?? { priority: 'NORMAL' };
|
|
83
|
+
const proxyServices = this.taskProxies.get(key) ?? [];
|
|
84
|
+
// Capture ALS context from all registered storages
|
|
85
|
+
const alsContext = {};
|
|
86
|
+
for (const [i, als] of (this.options.asyncLocalStorages ?? []).entries()) {
|
|
87
|
+
const store = als.getStore();
|
|
88
|
+
if (store !== undefined)
|
|
89
|
+
alsContext[String(i)] = store;
|
|
90
|
+
}
|
|
91
|
+
// Capture OTEL trace context if available
|
|
92
|
+
const traceContext = captureTraceContext();
|
|
93
|
+
// Generate a unique signal ID if an AbortSignal was provided
|
|
94
|
+
const abortSignalId = options.signal ? node_crypto_1.default.randomUUID() : undefined;
|
|
111
95
|
return this.pool.execute({
|
|
96
|
+
jobId: node_crypto_1.default.randomUUID(),
|
|
112
97
|
serviceName,
|
|
113
98
|
methodName,
|
|
114
99
|
args,
|
|
115
|
-
priority:
|
|
116
|
-
timeout:
|
|
117
|
-
|
|
100
|
+
priority: options.priority ?? defaults.priority,
|
|
101
|
+
timeout: options.timeout ?? defaults.timeout,
|
|
102
|
+
retry: options.retry ?? defaults.retry ?? 0,
|
|
103
|
+
retryDelay: options.retryDelay ?? defaults.retryDelay ?? 0,
|
|
104
|
+
proxyServices,
|
|
105
|
+
alsContext,
|
|
106
|
+
traceContext,
|
|
107
|
+
abortSignalId,
|
|
108
|
+
}, options.signal);
|
|
109
|
+
}
|
|
110
|
+
/** Listen for dead-letter events (jobs that exhausted all retry attempts) */
|
|
111
|
+
onDead(listener) {
|
|
112
|
+
this.pool?.on('dead', listener);
|
|
113
|
+
return this;
|
|
114
|
+
}
|
|
115
|
+
/** Listen for task lifecycle events */
|
|
116
|
+
onTaskEnd(listener) {
|
|
117
|
+
this.pool?.on('taskEnd', listener);
|
|
118
|
+
return this;
|
|
119
|
+
}
|
|
120
|
+
onTaskStart(listener) {
|
|
121
|
+
this.pool?.on('taskStart', listener);
|
|
122
|
+
return this;
|
|
123
|
+
}
|
|
124
|
+
onTaskError(listener) {
|
|
125
|
+
this.pool?.on('taskError', listener);
|
|
126
|
+
return this;
|
|
127
|
+
}
|
|
128
|
+
/** Current pool stats — use for health checks and metrics */
|
|
129
|
+
stats() {
|
|
130
|
+
return this.pool?.stats() ?? {
|
|
131
|
+
poolSize: this.options.poolSize ?? 0,
|
|
132
|
+
idle: 0, busy: 0, queued: 0, warmingUp: 0,
|
|
133
|
+
};
|
|
118
134
|
}
|
|
119
|
-
/**
|
|
120
|
-
* Gracefully shuts down worker pool.
|
|
121
|
-
*
|
|
122
|
-
* Called automatically by NestJS
|
|
123
|
-
* during application shutdown.
|
|
124
|
-
*/
|
|
125
135
|
async onModuleDestroy() {
|
|
126
136
|
await this.pool?.destroy();
|
|
127
137
|
}
|
|
128
138
|
};
|
|
129
139
|
exports.WorkerService = WorkerService;
|
|
130
|
-
exports.WorkerService = WorkerService = __decorate([
|
|
140
|
+
exports.WorkerService = WorkerService = WorkerService_1 = __decorate([
|
|
131
141
|
(0, common_1.Injectable)(),
|
|
132
142
|
__param(1, (0, common_1.Inject)('WORKER_OPTIONS')),
|
|
133
143
|
__metadata("design:paramtypes", [discovery_service_1.WorkerDiscoveryService, Object])
|
|
134
144
|
], WorkerService);
|
|
145
|
+
/**
|
|
146
|
+
* Capture the active OpenTelemetry trace context if @opentelemetry/api is
|
|
147
|
+
* available. Returns an empty object otherwise — no hard dependency.
|
|
148
|
+
*/
|
|
149
|
+
function captureTraceContext() {
|
|
150
|
+
try {
|
|
151
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
152
|
+
const api = require('@opentelemetry/api');
|
|
153
|
+
const carrier = {};
|
|
154
|
+
api.propagation.inject(api.context.active(), carrier);
|
|
155
|
+
return carrier;
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return {};
|
|
159
|
+
}
|
|
160
|
+
}
|
|
135
161
|
//# sourceMappingURL=worker.service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.service.js","sourceRoot":"","sources":["../../src/core/worker.service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"worker.service.js","sourceRoot":"","sources":["../../src/core/worker.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,36 +2,18 @@ import 'reflect-metadata';
|
|
|
2
2
|
export declare const WORKER_CLASS_META = "worker:class";
|
|
3
3
|
export declare const WORKER_METHOD_META = "worker:method";
|
|
4
4
|
export declare const WORKER_DEPS_META = "worker:deps";
|
|
5
|
+
export declare const WORKER_PROXY_META = "worker:proxy";
|
|
5
6
|
export interface WorkerTaskOptions {
|
|
6
7
|
priority?: 'HIGH' | 'NORMAL' | 'LOW';
|
|
7
8
|
timeout?: number;
|
|
9
|
+
/** Number of additional attempts after the first failure. Default: 0. */
|
|
10
|
+
retry?: number;
|
|
11
|
+
/** Delay in ms between retry attempts. Supports exponential backoff
|
|
12
|
+
* when set as a function: (attempt) => attempt * 1000 */
|
|
13
|
+
retryDelay?: number | ((attempt: number) => number);
|
|
8
14
|
}
|
|
9
|
-
/**
|
|
10
|
-
* @WorkerClass() – marks a provider as a container of worker tasks.
|
|
11
|
-
*
|
|
12
|
-
* The discovery service scans all NestJS providers for this marker,
|
|
13
|
-
* then inspects each method for @WorkerTask().
|
|
14
|
-
*
|
|
15
|
-
* Declare any injectable deps that your @WorkerTask methods need via
|
|
16
|
-
* the `deps` option. These are resolved from the live NestJS container,
|
|
17
|
-
* serialised, and reconstructed inside each worker thread.
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* @WorkerClass({ deps: [ConfigService] })
|
|
21
|
-
* export class ImageService { ... }
|
|
22
|
-
*/
|
|
23
15
|
export declare function WorkerClass(options?: {
|
|
24
16
|
deps?: (abstract new (...a: unknown[]) => unknown)[];
|
|
17
|
+
proxy?: (abstract new (...a: unknown[]) => unknown)[];
|
|
25
18
|
}): ClassDecorator;
|
|
26
|
-
/**
|
|
27
|
-
* @WorkerTask() – marks a method to be offloaded to a worker thread.
|
|
28
|
-
*
|
|
29
|
-
* The method must be pure with respect to thread-crossing:
|
|
30
|
-
* its arguments and return value must be structured-clone compatible.
|
|
31
|
-
* Any NestJS deps it needs should be declared on @WorkerClass({ deps }).
|
|
32
|
-
*
|
|
33
|
-
* @example
|
|
34
|
-
* @WorkerTask({ priority: 'HIGH', timeout: 5000 })
|
|
35
|
-
* resizeImage(value: number): number { ... }
|
|
36
|
-
*/
|
|
37
19
|
export declare function WorkerTask(options?: WorkerTaskOptions): MethodDecorator;
|
|
@@ -1,45 +1,24 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.WORKER_DEPS_META = exports.WORKER_METHOD_META = exports.WORKER_CLASS_META = void 0;
|
|
3
|
+
exports.WORKER_PROXY_META = exports.WORKER_DEPS_META = exports.WORKER_METHOD_META = exports.WORKER_CLASS_META = void 0;
|
|
4
4
|
exports.WorkerClass = WorkerClass;
|
|
5
5
|
exports.WorkerTask = WorkerTask;
|
|
6
6
|
require("reflect-metadata");
|
|
7
7
|
exports.WORKER_CLASS_META = 'worker:class';
|
|
8
8
|
exports.WORKER_METHOD_META = 'worker:method';
|
|
9
9
|
exports.WORKER_DEPS_META = 'worker:deps';
|
|
10
|
-
|
|
11
|
-
* @WorkerClass() – marks a provider as a container of worker tasks.
|
|
12
|
-
*
|
|
13
|
-
* The discovery service scans all NestJS providers for this marker,
|
|
14
|
-
* then inspects each method for @WorkerTask().
|
|
15
|
-
*
|
|
16
|
-
* Declare any injectable deps that your @WorkerTask methods need via
|
|
17
|
-
* the `deps` option. These are resolved from the live NestJS container,
|
|
18
|
-
* serialised, and reconstructed inside each worker thread.
|
|
19
|
-
*
|
|
20
|
-
* @example
|
|
21
|
-
* @WorkerClass({ deps: [ConfigService] })
|
|
22
|
-
* export class ImageService { ... }
|
|
23
|
-
*/
|
|
10
|
+
exports.WORKER_PROXY_META = 'worker:proxy';
|
|
24
11
|
function WorkerClass(options = {}) {
|
|
25
12
|
return (target) => {
|
|
26
13
|
Reflect.defineMetadata(exports.WORKER_CLASS_META, true, target);
|
|
27
14
|
if (options.deps?.length) {
|
|
28
15
|
Reflect.defineMetadata(exports.WORKER_DEPS_META, options.deps, target);
|
|
29
16
|
}
|
|
17
|
+
if (options.proxy?.length) {
|
|
18
|
+
Reflect.defineMetadata(exports.WORKER_PROXY_META, options.proxy, target);
|
|
19
|
+
}
|
|
30
20
|
};
|
|
31
21
|
}
|
|
32
|
-
/**
|
|
33
|
-
* @WorkerTask() – marks a method to be offloaded to a worker thread.
|
|
34
|
-
*
|
|
35
|
-
* The method must be pure with respect to thread-crossing:
|
|
36
|
-
* its arguments and return value must be structured-clone compatible.
|
|
37
|
-
* Any NestJS deps it needs should be declared on @WorkerClass({ deps }).
|
|
38
|
-
*
|
|
39
|
-
* @example
|
|
40
|
-
* @WorkerTask({ priority: 'HIGH', timeout: 5000 })
|
|
41
|
-
* resizeImage(value: number): number { ... }
|
|
42
|
-
*/
|
|
43
22
|
function WorkerTask(options = {}) {
|
|
44
23
|
return (target, propertyKey) => {
|
|
45
24
|
Reflect.defineMetadata(exports.WORKER_METHOD_META, options, target, propertyKey);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker-task.decorator.js","sourceRoot":"","sources":["../../src/decorators/worker-task.decorator.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"worker-task.decorator.js","sourceRoot":"","sources":["../../src/decorators/worker-task.decorator.ts"],"names":[],"mappings":";;;AAiBA,kCAeC;AAED,gCAIC;AAtCD,4BAA0B;AAEb,QAAA,iBAAiB,GAAI,cAAc,CAAC;AACpC,QAAA,kBAAkB,GAAG,eAAe,CAAC;AACrC,QAAA,gBAAgB,GAAK,aAAa,CAAC;AACnC,QAAA,iBAAiB,GAAI,cAAc,CAAC;AAYjD,SAAgB,WAAW,CACzB,UAGI,EAAE;IAEN,OAAO,CAAC,MAAM,EAAE,EAAE;QAChB,OAAO,CAAC,cAAc,CAAC,yBAAiB,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QACxD,IAAI,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;YACzB,OAAO,CAAC,cAAc,CAAC,wBAAgB,EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACjE,CAAC;QACD,IAAI,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;YAC1B,OAAO,CAAC,cAAc,CAAC,yBAAiB,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QACnE,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED,SAAgB,UAAU,CAAC,UAA6B,EAAE;IACxD,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE;QAC7B,OAAO,CAAC,cAAc,CAAC,0BAAkB,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IAC3E,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -1,20 +1,13 @@
|
|
|
1
1
|
import { ModuleRef, ModulesContainer } from '@nestjs/core';
|
|
2
2
|
import type { DiscoveredTask } from '../core/worker.interfaces';
|
|
3
|
-
/**
|
|
4
|
-
* WorkerDiscoveryService
|
|
5
|
-
*
|
|
6
|
-
* Scans both module.providers AND module.controllers so that
|
|
7
|
-
* @WorkerClass() / @WorkerTask() work on NestJS controllers too.
|
|
8
|
-
*
|
|
9
|
-
* scan() is called lazily by WorkerService on the first run() call,
|
|
10
|
-
* guaranteeing all providers and controllers are fully instantiated.
|
|
11
|
-
*/
|
|
12
3
|
export declare class WorkerDiscoveryService {
|
|
13
4
|
private readonly modulesContainer;
|
|
14
5
|
private readonly moduleRef;
|
|
6
|
+
private readonly logger;
|
|
15
7
|
private scanned;
|
|
16
8
|
private discovered;
|
|
17
9
|
constructor(modulesContainer: ModulesContainer, moduleRef: ModuleRef);
|
|
18
10
|
scan(): DiscoveredTask[];
|
|
19
11
|
private inspectWrapper;
|
|
12
|
+
private resolveToken;
|
|
20
13
|
}
|
|
@@ -8,23 +8,16 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
8
8
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
9
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
10
|
};
|
|
11
|
+
var WorkerDiscoveryService_1;
|
|
11
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
13
|
exports.WorkerDiscoveryService = void 0;
|
|
13
14
|
const common_1 = require("@nestjs/common");
|
|
14
15
|
const core_1 = require("@nestjs/core");
|
|
15
16
|
const worker_task_decorator_1 = require("../decorators/worker-task.decorator");
|
|
16
|
-
|
|
17
|
-
* WorkerDiscoveryService
|
|
18
|
-
*
|
|
19
|
-
* Scans both module.providers AND module.controllers so that
|
|
20
|
-
* @WorkerClass() / @WorkerTask() work on NestJS controllers too.
|
|
21
|
-
*
|
|
22
|
-
* scan() is called lazily by WorkerService on the first run() call,
|
|
23
|
-
* guaranteeing all providers and controllers are fully instantiated.
|
|
24
|
-
*/
|
|
25
|
-
let WorkerDiscoveryService = class WorkerDiscoveryService {
|
|
17
|
+
let WorkerDiscoveryService = WorkerDiscoveryService_1 = class WorkerDiscoveryService {
|
|
26
18
|
modulesContainer;
|
|
27
19
|
moduleRef;
|
|
20
|
+
logger = new common_1.Logger(WorkerDiscoveryService_1.name);
|
|
28
21
|
scanned = false;
|
|
29
22
|
discovered = [];
|
|
30
23
|
constructor(modulesContainer, moduleRef) {
|
|
@@ -36,59 +29,131 @@ let WorkerDiscoveryService = class WorkerDiscoveryService {
|
|
|
36
29
|
return this.discovered;
|
|
37
30
|
this.scanned = true;
|
|
38
31
|
for (const module of this.modulesContainer.values()) {
|
|
39
|
-
// Scan providers (services, repositories, etc.)
|
|
40
32
|
for (const wrapper of module.providers.values()) {
|
|
41
33
|
this.inspectWrapper(wrapper);
|
|
42
34
|
}
|
|
43
|
-
// Scan controllers — NestJS registers these separately
|
|
44
35
|
for (const wrapper of module.controllers.values()) {
|
|
45
36
|
this.inspectWrapper(wrapper);
|
|
46
37
|
}
|
|
47
38
|
}
|
|
39
|
+
if (this.discovered.length === 0) {
|
|
40
|
+
this.logger.warn('No @WorkerTask methods found. Ensure at least one class is decorated ' +
|
|
41
|
+
'with @WorkerClass() and has methods decorated with @WorkerTask().');
|
|
42
|
+
}
|
|
48
43
|
return this.discovered;
|
|
49
44
|
}
|
|
50
45
|
inspectWrapper(wrapper) {
|
|
51
46
|
const { instance, metatype } = wrapper;
|
|
52
47
|
if (!instance || !metatype)
|
|
53
48
|
return;
|
|
54
|
-
|
|
55
|
-
if (!isWorkerClass)
|
|
49
|
+
if (!Reflect.getMetadata(worker_task_decorator_1.WORKER_CLASS_META, metatype))
|
|
56
50
|
return;
|
|
57
51
|
const serviceName = metatype.name;
|
|
58
|
-
const proto =
|
|
52
|
+
const proto = metatype.prototype;
|
|
53
|
+
// ── serialised deps ───────────────────────────────────────────────────
|
|
59
54
|
const depTypes = Reflect.getMetadata(worker_task_decorator_1.WORKER_DEPS_META, metatype) ?? [];
|
|
60
|
-
const deps = depTypes.map((token) =>
|
|
61
|
-
|
|
62
|
-
|
|
55
|
+
const deps = depTypes.map((token) => this.resolveToken(token, serviceName, 'deps'));
|
|
56
|
+
// ── proxy services ────────────────────────────────────────────────────
|
|
57
|
+
const proxyTypes = Reflect.getMetadata(worker_task_decorator_1.WORKER_PROXY_META, metatype) ?? [];
|
|
58
|
+
const proxyInstances = proxyTypes.map((token) => {
|
|
59
|
+
const svcInstance = this.resolveToken(token, serviceName, 'proxy');
|
|
60
|
+
const tokenName = token.name ?? 'unknown';
|
|
61
|
+
const propertyKey = findPropertyKey(instance, svcInstance) ?? camelCase(tokenName);
|
|
62
|
+
if (!findPropertyKey(instance, svcInstance)) {
|
|
63
|
+
this.logger.warn(`${serviceName}: could not find property key for proxy "${tokenName}" ` +
|
|
64
|
+
`— falling back to camelCase: "${propertyKey}".`);
|
|
63
65
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
const methodNames = [];
|
|
67
|
+
if (svcInstance) {
|
|
68
|
+
let cursor = Object.getPrototypeOf(svcInstance);
|
|
69
|
+
while (cursor && cursor !== Object.prototype) {
|
|
70
|
+
for (const k of Object.getOwnPropertyNames(cursor)) {
|
|
71
|
+
if (k !== 'constructor' && !methodNames.includes(k)) {
|
|
72
|
+
const desc = Object.getOwnPropertyDescriptor(cursor, k);
|
|
73
|
+
if (desc && typeof desc.value === 'function')
|
|
74
|
+
methodNames.push(k);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
cursor = Object.getPrototypeOf(cursor);
|
|
78
|
+
}
|
|
66
79
|
}
|
|
80
|
+
return { propertyKey, methodNames, instance: svcInstance ?? {} };
|
|
67
81
|
});
|
|
82
|
+
// ── @WorkerTask methods ───────────────────────────────────────────────
|
|
83
|
+
let tasksFound = 0;
|
|
68
84
|
for (const methodName of Object.getOwnPropertyNames(proto)) {
|
|
69
85
|
if (methodName === 'constructor')
|
|
70
86
|
continue;
|
|
71
87
|
const options = Reflect.getMetadata(worker_task_decorator_1.WORKER_METHOD_META, proto, methodName);
|
|
72
88
|
if (!options)
|
|
73
89
|
continue;
|
|
90
|
+
tasksFound++;
|
|
91
|
+
// Normalise retryDelay: function form → serialised as max of first 3 calls
|
|
92
|
+
// (functions can't cross the thread boundary; we store the numeric value)
|
|
93
|
+
let retryDelay;
|
|
94
|
+
if (typeof options.retryDelay === 'function') {
|
|
95
|
+
// Store delays for attempts 1-3 as a simple average to give workers
|
|
96
|
+
// a representative delay. For production use, pass a number.
|
|
97
|
+
const fn = options.retryDelay;
|
|
98
|
+
retryDelay = Math.round((fn(1) + fn(2) + fn(3)) / 3);
|
|
99
|
+
this.logger.warn(`${serviceName}.${methodName}: retryDelay as a function is not supported ` +
|
|
100
|
+
`across thread boundaries. Computed average: ${retryDelay}ms. ` +
|
|
101
|
+
`Pass a number for precise control.`);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
retryDelay = options.retryDelay;
|
|
105
|
+
}
|
|
74
106
|
const fn = instance[methodName].bind(instance);
|
|
75
107
|
this.discovered.push({
|
|
76
108
|
serviceName,
|
|
77
109
|
methodName,
|
|
78
110
|
priority: options.priority ?? 'NORMAL',
|
|
79
111
|
timeout: options.timeout,
|
|
112
|
+
retry: options.retry,
|
|
113
|
+
retryDelay,
|
|
80
114
|
fn,
|
|
81
115
|
metatype: metatype,
|
|
82
116
|
instance,
|
|
83
117
|
deps,
|
|
118
|
+
proxyInstances,
|
|
84
119
|
});
|
|
120
|
+
this.logger.debug(`Registered task: ${serviceName}.${methodName} ` +
|
|
121
|
+
`[priority=${options.priority ?? 'NORMAL'}` +
|
|
122
|
+
`${options.timeout ? `, timeout=${options.timeout}ms` : ''}` +
|
|
123
|
+
`${options.retry ? `, retry=${options.retry}` : ''}]`);
|
|
124
|
+
}
|
|
125
|
+
if (tasksFound === 0) {
|
|
126
|
+
this.logger.warn(`${serviceName} has @WorkerClass() but no @WorkerTask() methods.`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
resolveToken(token, ownerName, role) {
|
|
130
|
+
try {
|
|
131
|
+
return this.moduleRef.get(token, { strict: false });
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
const name = token.name ?? String(token);
|
|
135
|
+
this.logger.error(`${ownerName}: failed to resolve ${role} token "${name}". ` +
|
|
136
|
+
`Original error: ${err.message}`);
|
|
137
|
+
return undefined;
|
|
85
138
|
}
|
|
86
139
|
}
|
|
87
140
|
};
|
|
88
141
|
exports.WorkerDiscoveryService = WorkerDiscoveryService;
|
|
89
|
-
exports.WorkerDiscoveryService = WorkerDiscoveryService = __decorate([
|
|
142
|
+
exports.WorkerDiscoveryService = WorkerDiscoveryService = WorkerDiscoveryService_1 = __decorate([
|
|
90
143
|
(0, common_1.Injectable)(),
|
|
91
144
|
__metadata("design:paramtypes", [core_1.ModulesContainer,
|
|
92
145
|
core_1.ModuleRef])
|
|
93
146
|
], WorkerDiscoveryService);
|
|
147
|
+
function findPropertyKey(serviceInstance, depInstance) {
|
|
148
|
+
if (!serviceInstance || !depInstance || typeof serviceInstance !== 'object')
|
|
149
|
+
return undefined;
|
|
150
|
+
for (const key of Object.getOwnPropertyNames(serviceInstance)) {
|
|
151
|
+
if (serviceInstance[key] === depInstance)
|
|
152
|
+
return key;
|
|
153
|
+
}
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
function camelCase(name) {
|
|
157
|
+
return name.charAt(0).toLowerCase() + name.slice(1);
|
|
158
|
+
}
|
|
94
159
|
//# sourceMappingURL=discovery.service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discovery.service.js","sourceRoot":"","sources":["../../src/discovery/discovery.service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"discovery.service.js","sourceRoot":"","sources":["../../src/discovery/discovery.service.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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"}
|