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
|
@@ -11,51 +11,32 @@ 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 WorkerService_1;
|
|
14
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
16
|
exports.WorkerService = void 0;
|
|
16
17
|
const common_1 = require("@nestjs/common");
|
|
17
18
|
const worker_pool_1 = require("./worker.pool");
|
|
18
19
|
const discovery_service_1 = require("../discovery/discovery.service");
|
|
19
20
|
const di_serializer_1 = require("../di/di-serializer");
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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 {
|
|
21
|
+
// Monotonic ID generator — far cheaper than crypto.randomUUID() and unique
|
|
22
|
+
// per-process which is all we need (jobs never leave this process).
|
|
23
|
+
let __jobIdCounter = 0;
|
|
24
|
+
const __jobIdPrefix = `j${Date.now().toString(36)}-`;
|
|
25
|
+
const nextId = () => __jobIdPrefix + (++__jobIdCounter).toString(36);
|
|
26
|
+
// Hot-path shared frozen sentinels — let v8 elide allocations on the common
|
|
27
|
+
// "no proxies / no ALS context" path.
|
|
28
|
+
const EMPTY_PROXIES = Object.freeze([]);
|
|
29
|
+
const EMPTY_ALS = Object.freeze({});
|
|
30
|
+
const DEFAULT_TASK = Object.freeze({ priority: 'NORMAL' });
|
|
31
|
+
let WorkerService = WorkerService_1 = class WorkerService {
|
|
55
32
|
discovery;
|
|
56
33
|
options;
|
|
34
|
+
logger = new common_1.Logger(WorkerService_1.name);
|
|
57
35
|
pool = null;
|
|
58
|
-
|
|
36
|
+
taskDefaults = new Map();
|
|
37
|
+
taskProxies = new Map();
|
|
38
|
+
/** Cached array of ALS storages — avoids `?? []` + entries() per call. */
|
|
39
|
+
alsStorages = [];
|
|
59
40
|
constructor(discovery, options) {
|
|
60
41
|
this.discovery = discovery;
|
|
61
42
|
this.options = options;
|
|
@@ -66,70 +47,142 @@ let WorkerService = class WorkerService {
|
|
|
66
47
|
initPool() {
|
|
67
48
|
if (this.pool)
|
|
68
49
|
return;
|
|
50
|
+
this.alsStorages = (this.options.asyncLocalStorages ?? []);
|
|
69
51
|
const tasks = this.discovery.scan();
|
|
52
|
+
const proxyMap = new Map();
|
|
70
53
|
for (const task of tasks) {
|
|
71
|
-
|
|
54
|
+
const key = `${task.serviceName}.${task.methodName}`;
|
|
55
|
+
this.taskDefaults.set(key, {
|
|
56
|
+
priority: task.priority,
|
|
57
|
+
timeout: task.timeout,
|
|
58
|
+
retry: task.retry,
|
|
59
|
+
retryDelay: task.retryDelay,
|
|
60
|
+
});
|
|
61
|
+
const descriptors = task.proxyInstances.map((p) => {
|
|
62
|
+
if (!proxyMap.has(p.propertyKey)) {
|
|
63
|
+
proxyMap.set(p.propertyKey, { methodNames: p.methodNames, instance: p.instance });
|
|
64
|
+
}
|
|
65
|
+
return { propertyKey: p.propertyKey, methodNames: p.methodNames };
|
|
66
|
+
});
|
|
67
|
+
this.taskProxies.set(key, descriptors);
|
|
72
68
|
}
|
|
73
69
|
const serialized = (0, di_serializer_1.serializeForWorker)(tasks);
|
|
74
|
-
|
|
70
|
+
const proxyInstances = Array.from(proxyMap.entries()).map(([propertyKey, { methodNames, instance }]) => ({ propertyKey, methodNames, instance }));
|
|
71
|
+
this.pool = new worker_pool_1.WorkerPool(serialized, proxyInstances, this.options.poolSize, this.options.shutdownTimeout);
|
|
72
|
+
// Forward pool events to logger and expose via EventEmitter
|
|
73
|
+
this.pool.on('dead', (event) => {
|
|
74
|
+
this.logger.error(`Dead letter: ${event.serviceName}.${event.methodName} ` +
|
|
75
|
+
`failed after ${event.attempts} attempt(s) — ${event.error.message}`);
|
|
76
|
+
});
|
|
77
|
+
this.pool.on('error', (err) => {
|
|
78
|
+
this.logger.error(`Worker pool error: ${err.message}`, err.stack);
|
|
79
|
+
});
|
|
75
80
|
}
|
|
76
81
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* Parameters:
|
|
80
|
-
* - serviceName: target service
|
|
81
|
-
* - methodName: target method
|
|
82
|
-
* - args: serialized arguments
|
|
83
|
-
* - overrides: runtime priority/timeout overrides
|
|
84
|
-
*
|
|
85
|
-
* Priority:
|
|
86
|
-
* - HIGH
|
|
87
|
-
* - NORMAL
|
|
88
|
-
* - LOW
|
|
89
|
-
*
|
|
90
|
-
* Timeout:
|
|
91
|
-
* Automatically terminates timed-out workers.
|
|
82
|
+
* Run a @WorkerTask method in a worker thread.
|
|
92
83
|
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
* 'ImageService',
|
|
98
|
-
* 'generateThumbnail',
|
|
99
|
-
* [1920, 1080],
|
|
100
|
-
* {
|
|
101
|
-
* priority: 'HIGH',
|
|
102
|
-
* timeout: 3000,
|
|
103
|
-
* },
|
|
104
|
-
* );
|
|
105
|
-
* ```
|
|
84
|
+
* @param serviceName Class name of the @WorkerClass provider
|
|
85
|
+
* @param methodName Method decorated with @WorkerTask
|
|
86
|
+
* @param args structuredClone-compatible arguments
|
|
87
|
+
* @param options Optional priority / timeout / retry / AbortSignal overrides
|
|
106
88
|
*/
|
|
107
|
-
run(serviceName, methodName, args = [],
|
|
89
|
+
run(serviceName, methodName, args = [], options = {}) {
|
|
108
90
|
const key = `${serviceName}.${methodName}`;
|
|
109
|
-
const defaults = this.
|
|
110
|
-
|
|
91
|
+
const defaults = this.taskDefaults.get(key) ?? DEFAULT_TASK;
|
|
92
|
+
const proxyServices = this.taskProxies.get(key) ?? EMPTY_PROXIES;
|
|
93
|
+
// Capture ALS context only if any storages are registered. Empty objects
|
|
94
|
+
// still cost allocation + a structuredClone hop across the worker boundary.
|
|
95
|
+
let alsContext;
|
|
96
|
+
const storages = this.alsStorages;
|
|
97
|
+
for (let i = 0, len = storages.length; i < len; i++) {
|
|
98
|
+
const store = storages[i].getStore();
|
|
99
|
+
if (store !== undefined) {
|
|
100
|
+
(alsContext ??= {})[i < 10 ? String.fromCharCode(48 + i) : String(i)] = store;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Capture OTEL trace context if available (lazy, cached lookup).
|
|
104
|
+
const traceContext = captureTraceContext();
|
|
105
|
+
// Generate a unique signal ID if an AbortSignal was provided
|
|
106
|
+
const abortSignalId = options.signal ? nextId() : undefined;
|
|
111
107
|
return this.pool.execute({
|
|
108
|
+
jobId: nextId(),
|
|
112
109
|
serviceName,
|
|
113
110
|
methodName,
|
|
114
111
|
args,
|
|
115
|
-
priority:
|
|
116
|
-
timeout:
|
|
117
|
-
|
|
112
|
+
priority: options.priority ?? defaults.priority,
|
|
113
|
+
timeout: options.timeout ?? defaults.timeout,
|
|
114
|
+
retry: options.retry ?? defaults.retry ?? 0,
|
|
115
|
+
retryDelay: options.retryDelay ?? defaults.retryDelay ?? 0,
|
|
116
|
+
proxyServices,
|
|
117
|
+
alsContext: alsContext ?? EMPTY_ALS,
|
|
118
|
+
traceContext,
|
|
119
|
+
abortSignalId,
|
|
120
|
+
}, options.signal);
|
|
121
|
+
}
|
|
122
|
+
/** Listen for dead-letter events (jobs that exhausted all retry attempts) */
|
|
123
|
+
onDead(listener) {
|
|
124
|
+
this.pool?.on('dead', listener);
|
|
125
|
+
return this;
|
|
126
|
+
}
|
|
127
|
+
/** Listen for task lifecycle events */
|
|
128
|
+
onTaskEnd(listener) {
|
|
129
|
+
this.pool?.on('taskEnd', listener);
|
|
130
|
+
return this;
|
|
131
|
+
}
|
|
132
|
+
onTaskStart(listener) {
|
|
133
|
+
this.pool?.on('taskStart', listener);
|
|
134
|
+
return this;
|
|
135
|
+
}
|
|
136
|
+
onTaskError(listener) {
|
|
137
|
+
this.pool?.on('taskError', listener);
|
|
138
|
+
return this;
|
|
139
|
+
}
|
|
140
|
+
/** Current pool stats — use for health checks and metrics */
|
|
141
|
+
stats() {
|
|
142
|
+
return this.pool?.stats() ?? {
|
|
143
|
+
poolSize: this.options.poolSize ?? 0,
|
|
144
|
+
idle: 0, busy: 0, queued: 0, warmingUp: 0,
|
|
145
|
+
};
|
|
118
146
|
}
|
|
119
|
-
/**
|
|
120
|
-
* Gracefully shuts down worker pool.
|
|
121
|
-
*
|
|
122
|
-
* Called automatically by NestJS
|
|
123
|
-
* during application shutdown.
|
|
124
|
-
*/
|
|
125
147
|
async onModuleDestroy() {
|
|
126
148
|
await this.pool?.destroy();
|
|
127
149
|
}
|
|
128
150
|
};
|
|
129
151
|
exports.WorkerService = WorkerService;
|
|
130
|
-
exports.WorkerService = WorkerService = __decorate([
|
|
152
|
+
exports.WorkerService = WorkerService = WorkerService_1 = __decorate([
|
|
131
153
|
(0, common_1.Injectable)(),
|
|
132
154
|
__param(1, (0, common_1.Inject)('WORKER_OPTIONS')),
|
|
133
155
|
__metadata("design:paramtypes", [discovery_service_1.WorkerDiscoveryService, Object])
|
|
134
156
|
], WorkerService);
|
|
157
|
+
/**
|
|
158
|
+
* Capture the active OpenTelemetry trace context if @opentelemetry/api is
|
|
159
|
+
* available. Returns an empty object otherwise — no hard dependency.
|
|
160
|
+
*
|
|
161
|
+
* The require() lookup is performed at most once and cached, since it shows
|
|
162
|
+
* up on the hot path of every `run()` call.
|
|
163
|
+
*/
|
|
164
|
+
const EMPTY_TRACE = Object.freeze({});
|
|
165
|
+
let __otelApi;
|
|
166
|
+
function captureTraceContext() {
|
|
167
|
+
if (__otelApi === undefined) {
|
|
168
|
+
try {
|
|
169
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
170
|
+
__otelApi = require('@opentelemetry/api');
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
__otelApi = null;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (!__otelApi)
|
|
177
|
+
return EMPTY_TRACE;
|
|
178
|
+
const carrier = {};
|
|
179
|
+
__otelApi.propagation.inject(__otelApi.context.active(), carrier);
|
|
180
|
+
// Return the shared empty object when nothing was injected to avoid
|
|
181
|
+
// a per-call structuredClone of an empty object across the worker boundary.
|
|
182
|
+
for (const _k in carrier) {
|
|
183
|
+
void _k;
|
|
184
|
+
return carrier;
|
|
185
|
+
}
|
|
186
|
+
return EMPTY_TRACE;
|
|
187
|
+
}
|
|
135
188
|
//# 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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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Throughput / latency micro-benchmark.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* npx ts-node src/example/bench.ts # default 5000 tasks
|
|
6
|
+
* TASKS=20000 POOL=8 npx ts-node src/example/bench.ts
|
|
7
|
+
*
|
|
8
|
+
* Reports:
|
|
9
|
+
* - cold-start time (until pool is ready for first task)
|
|
10
|
+
* - p50 / p95 / p99 / max round-trip latency
|
|
11
|
+
* - sustained throughput (tasks / second)
|
|
12
|
+
*/
|
|
13
|
+
import 'reflect-metadata';
|