nestworker 1.1.21 → 2.0.5
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/LICENSE +21 -21
- package/README.md +335 -143
- package/dist/core/worker.interfaces.d.ts +36 -0
- package/dist/{models/task.js → core/worker.interfaces.js} +1 -1
- package/dist/core/worker.interfaces.js.map +1 -0
- package/dist/core/worker.module.d.ts +15 -0
- package/dist/core/worker.module.js +43 -0
- package/dist/core/worker.module.js.map +1 -0
- package/dist/core/worker.pool.d.ts +20 -0
- package/dist/core/worker.pool.js +210 -0
- package/dist/core/worker.pool.js.map +1 -0
- package/dist/core/worker.service.d.ts +88 -0
- package/dist/core/worker.service.js +135 -0
- package/dist/core/worker.service.js.map +1 -0
- package/dist/decorators/worker-task.decorator.d.ts +37 -0
- package/dist/decorators/worker-task.decorator.js +48 -0
- package/dist/decorators/worker-task.decorator.js.map +1 -0
- package/dist/di/di-serializer.d.ts +19 -0
- package/dist/di/di-serializer.js +102 -0
- package/dist/di/di-serializer.js.map +1 -0
- package/dist/di/worker-container.d.ts +60 -0
- package/dist/di/worker-container.js +170 -0
- package/dist/di/worker-container.js.map +1 -0
- package/dist/discovery/discovery.service.d.ts +20 -0
- package/dist/discovery/discovery.service.js +94 -0
- package/dist/discovery/discovery.service.js.map +1 -0
- package/dist/example/config.service.d.ts +13 -0
- package/dist/example/config.service.js +35 -0
- package/dist/example/config.service.js.map +1 -0
- package/dist/example/image.service.d.ts +13 -0
- package/dist/example/image.service.js +123 -0
- package/dist/example/image.service.js.map +1 -0
- package/dist/example/main.d.ts +1 -0
- package/dist/example/main.js +60 -0
- package/dist/example/main.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/worker/worker-runtime.js +55 -0
- package/dist/worker/worker-runtime.js.map +1 -0
- package/package.json +58 -55
- package/CONTRIBUTING.md +0 -33
- package/dist/main.d.ts +0 -3
- package/dist/main.js +0 -29
- package/dist/main.js.map +0 -1
- package/dist/models/index.d.ts +0 -1
- package/dist/models/index.js +0 -18
- package/dist/models/index.js.map +0 -1
- package/dist/models/task.d.ts +0 -5
- package/dist/models/task.js.map +0 -1
- package/dist/tsconfig.build.tsbuildinfo +0 -1
- package/dist/worker/executor.d.ts +0 -5
- package/dist/worker/executor.js +0 -28
- package/dist/worker/executor.js.map +0 -1
- package/dist/worker/skeleton.d.ts +0 -6
- package/dist/worker/skeleton.js +0 -35
- package/dist/worker/skeleton.js.map +0 -1
- package/dist/worker/worker.js +0 -6
- package/dist/worker/worker.js.map +0 -1
- package/nest-cli.json +0 -8
- package/tsconfig.build.json +0 -4
- package/tsconfig.json +0 -25
- /package/dist/worker/{worker.d.ts → worker-runtime.d.ts} +0 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.WorkerPool = void 0;
|
|
7
|
+
const node_worker_threads_1 = require("node:worker_threads");
|
|
8
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const PRIORITY_WEIGHT = {
|
|
11
|
+
HIGH: 3,
|
|
12
|
+
NORMAL: 2,
|
|
13
|
+
LOW: 1,
|
|
14
|
+
};
|
|
15
|
+
class WorkerPool {
|
|
16
|
+
services;
|
|
17
|
+
size;
|
|
18
|
+
workers = [];
|
|
19
|
+
idle = [];
|
|
20
|
+
queue = [];
|
|
21
|
+
destroyed = false;
|
|
22
|
+
active = new Map();
|
|
23
|
+
constructor(services, size = node_os_1.default.cpus().length) {
|
|
24
|
+
this.services = services;
|
|
25
|
+
this.size = size;
|
|
26
|
+
for (let i = 0; i < this.size; i++) {
|
|
27
|
+
const worker = this.spawnWorker();
|
|
28
|
+
this.workers.push(worker);
|
|
29
|
+
this.idle.push(worker);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
execute(job) {
|
|
33
|
+
if (this.destroyed) {
|
|
34
|
+
return Promise.reject(new Error('WorkerPool destroyed'));
|
|
35
|
+
}
|
|
36
|
+
return new Promise(async (resolve, reject) => {
|
|
37
|
+
const task = {
|
|
38
|
+
job,
|
|
39
|
+
resolve: resolve,
|
|
40
|
+
reject,
|
|
41
|
+
};
|
|
42
|
+
await this.preemptIfNeeded(task);
|
|
43
|
+
// ALWAYS enqueue first
|
|
44
|
+
// otherwise priority is bypassed
|
|
45
|
+
this.enqueue(task);
|
|
46
|
+
// process queue
|
|
47
|
+
this.schedule();
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
async preemptIfNeeded(incoming) {
|
|
51
|
+
if (incoming.job.priority !== 'HIGH') {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
for (const [worker, running] of this.active) {
|
|
55
|
+
if (running.priority === 'LOW') {
|
|
56
|
+
// stop LOW task
|
|
57
|
+
await worker.terminate();
|
|
58
|
+
// requeue interrupted LOW task
|
|
59
|
+
this.enqueue(running.task);
|
|
60
|
+
// remove dead worker
|
|
61
|
+
this.active.delete(worker);
|
|
62
|
+
const idx = this.workers.indexOf(worker);
|
|
63
|
+
if (idx >= 0) {
|
|
64
|
+
this.workers.splice(idx, 1);
|
|
65
|
+
}
|
|
66
|
+
// create replacement worker
|
|
67
|
+
const replacement = this.spawnWorker();
|
|
68
|
+
this.workers.push(replacement);
|
|
69
|
+
// dispatch HIGH immediately
|
|
70
|
+
this.dispatch(replacement, incoming);
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
enqueue(task) {
|
|
77
|
+
const weight = PRIORITY_WEIGHT[task.job.priority];
|
|
78
|
+
let lo = 0;
|
|
79
|
+
let hi = this.queue.length;
|
|
80
|
+
while (lo < hi) {
|
|
81
|
+
const mid = (lo + hi) >>> 1;
|
|
82
|
+
if (PRIORITY_WEIGHT[this.queue[mid].job.priority] < weight) {
|
|
83
|
+
hi = mid;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
lo = mid + 1;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
this.queue.splice(lo, 0, task);
|
|
90
|
+
}
|
|
91
|
+
schedule() {
|
|
92
|
+
if (this.destroyed)
|
|
93
|
+
return;
|
|
94
|
+
while (this.idle.length > 0 &&
|
|
95
|
+
this.queue.length > 0) {
|
|
96
|
+
const worker = this.idle.pop();
|
|
97
|
+
// highest priority task first
|
|
98
|
+
const task = this.queue.shift();
|
|
99
|
+
this.dispatch(worker, task);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
dispatch(worker, task) {
|
|
103
|
+
let settled = false;
|
|
104
|
+
let timeoutHandle;
|
|
105
|
+
const cleanup = () => {
|
|
106
|
+
worker.removeListener('message', onMessage);
|
|
107
|
+
worker.removeListener('error', onError);
|
|
108
|
+
worker.removeListener('exit', onExit);
|
|
109
|
+
if (timeoutHandle) {
|
|
110
|
+
clearTimeout(timeoutHandle);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
const recycle = () => {
|
|
114
|
+
cleanup();
|
|
115
|
+
if (this.destroyed)
|
|
116
|
+
return;
|
|
117
|
+
this.idle.push(worker);
|
|
118
|
+
// continue processing
|
|
119
|
+
this.schedule();
|
|
120
|
+
};
|
|
121
|
+
const settle = (fn) => {
|
|
122
|
+
if (settled)
|
|
123
|
+
return;
|
|
124
|
+
settled = true;
|
|
125
|
+
Promise.resolve(fn())
|
|
126
|
+
.catch(() => { })
|
|
127
|
+
.finally(() => {
|
|
128
|
+
recycle();
|
|
129
|
+
});
|
|
130
|
+
};
|
|
131
|
+
const onMessage = (result) => {
|
|
132
|
+
settle(() => {
|
|
133
|
+
if (result.ok) {
|
|
134
|
+
task.resolve(result.data);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
task.reject(new Error(result.error?.message ??
|
|
138
|
+
'Worker error'));
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
const onError = async (err) => {
|
|
143
|
+
settle(async () => {
|
|
144
|
+
task.reject(err);
|
|
145
|
+
await this.replaceWorker(worker);
|
|
146
|
+
});
|
|
147
|
+
};
|
|
148
|
+
const onExit = async (code) => {
|
|
149
|
+
if (this.destroyed)
|
|
150
|
+
return;
|
|
151
|
+
settle(async () => {
|
|
152
|
+
task.reject(new Error(`Worker exited unexpectedly with code ${code} ` +
|
|
153
|
+
`while running "${task.job.serviceName}.${task.job.methodName}"`));
|
|
154
|
+
await this.replaceWorker(worker);
|
|
155
|
+
});
|
|
156
|
+
};
|
|
157
|
+
worker.once('message', onMessage);
|
|
158
|
+
worker.once('error', onError);
|
|
159
|
+
worker.once('exit', onExit);
|
|
160
|
+
if (task.job.timeout &&
|
|
161
|
+
task.job.timeout > 0) {
|
|
162
|
+
timeoutHandle = setTimeout(async () => {
|
|
163
|
+
if (settled)
|
|
164
|
+
return;
|
|
165
|
+
settled = true;
|
|
166
|
+
cleanup();
|
|
167
|
+
task.reject(new Error(`Task "${task.job.serviceName}.${task.job.methodName}" timed out after ${task.job.timeout}ms`));
|
|
168
|
+
try {
|
|
169
|
+
await worker.terminate();
|
|
170
|
+
}
|
|
171
|
+
catch { }
|
|
172
|
+
await this.replaceWorker(worker);
|
|
173
|
+
this.schedule();
|
|
174
|
+
}, task.job.timeout);
|
|
175
|
+
}
|
|
176
|
+
worker.postMessage(task.job);
|
|
177
|
+
}
|
|
178
|
+
spawnWorker() {
|
|
179
|
+
return new node_worker_threads_1.Worker(node_path_1.default.resolve(__dirname, '../worker/worker-runtime.js'), {
|
|
180
|
+
workerData: {
|
|
181
|
+
services: this.services,
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
async replaceWorker(oldWorker) {
|
|
186
|
+
const workerIndex = this.workers.indexOf(oldWorker);
|
|
187
|
+
if (workerIndex >= 0) {
|
|
188
|
+
this.workers.splice(workerIndex, 1);
|
|
189
|
+
}
|
|
190
|
+
const idleIndex = this.idle.indexOf(oldWorker);
|
|
191
|
+
if (idleIndex >= 0) {
|
|
192
|
+
this.idle.splice(idleIndex, 1);
|
|
193
|
+
}
|
|
194
|
+
const newWorker = this.spawnWorker();
|
|
195
|
+
this.workers.push(newWorker);
|
|
196
|
+
this.idle.push(newWorker);
|
|
197
|
+
}
|
|
198
|
+
async destroy() {
|
|
199
|
+
this.destroyed = true;
|
|
200
|
+
for (const queued of this.queue) {
|
|
201
|
+
queued.reject(new Error('WorkerPool destroyed'));
|
|
202
|
+
}
|
|
203
|
+
this.queue.length = 0;
|
|
204
|
+
await Promise.allSettled(this.workers.map((worker) => worker.terminate()));
|
|
205
|
+
this.workers.length = 0;
|
|
206
|
+
this.idle.length = 0;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
exports.WorkerPool = WorkerPool;
|
|
210
|
+
//# sourceMappingURL=worker.pool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.pool.js","sourceRoot":"","sources":["../../src/core/worker.pool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { OnModuleDestroy, OnModuleInit } from '@nestjs/common';
|
|
2
|
+
import { WorkerDiscoveryService } from '../discovery/discovery.service';
|
|
3
|
+
import type { TaskPriority, WorkerModuleOptions } from './worker.interfaces';
|
|
4
|
+
/**
|
|
5
|
+
* WorkerService
|
|
6
|
+
*
|
|
7
|
+
* Main entry point for executing worker-thread tasks.
|
|
8
|
+
*
|
|
9
|
+
* Features:
|
|
10
|
+
* - Lazy worker pool initialization
|
|
11
|
+
* - Automatic worker discovery
|
|
12
|
+
* - Priority queue support
|
|
13
|
+
* - Timeout support
|
|
14
|
+
* - Worker thread execution
|
|
15
|
+
* - NestJS dependency integration
|
|
16
|
+
*
|
|
17
|
+
* Lifecycle:
|
|
18
|
+
* 1. Discovers worker-enabled services
|
|
19
|
+
* 2. Serializes metadata for worker runtime
|
|
20
|
+
* 3. Creates WorkerPool
|
|
21
|
+
* 4. Dispatches tasks to workers
|
|
22
|
+
* 5. Handles graceful shutdown
|
|
23
|
+
*
|
|
24
|
+
* Example:
|
|
25
|
+
*
|
|
26
|
+
* ```ts
|
|
27
|
+
* await workerService.run(
|
|
28
|
+
* 'ImageService',
|
|
29
|
+
* 'resizeImage',
|
|
30
|
+
* [500],
|
|
31
|
+
* {
|
|
32
|
+
* priority: 'HIGH',
|
|
33
|
+
* timeout: 5000,
|
|
34
|
+
* },
|
|
35
|
+
* );
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare class WorkerService implements OnModuleInit, OnModuleDestroy {
|
|
39
|
+
private readonly discovery;
|
|
40
|
+
private readonly options;
|
|
41
|
+
private pool;
|
|
42
|
+
private readonly taskOptions;
|
|
43
|
+
constructor(discovery: WorkerDiscoveryService, options: WorkerModuleOptions);
|
|
44
|
+
onModuleInit(): void;
|
|
45
|
+
private initPool;
|
|
46
|
+
/**
|
|
47
|
+
* Executes a worker task.
|
|
48
|
+
*
|
|
49
|
+
* Parameters:
|
|
50
|
+
* - serviceName: target service
|
|
51
|
+
* - methodName: target method
|
|
52
|
+
* - args: serialized arguments
|
|
53
|
+
* - overrides: runtime priority/timeout overrides
|
|
54
|
+
*
|
|
55
|
+
* Priority:
|
|
56
|
+
* - HIGH
|
|
57
|
+
* - NORMAL
|
|
58
|
+
* - LOW
|
|
59
|
+
*
|
|
60
|
+
* Timeout:
|
|
61
|
+
* Automatically terminates timed-out workers.
|
|
62
|
+
*
|
|
63
|
+
* Example:
|
|
64
|
+
*
|
|
65
|
+
* ```ts
|
|
66
|
+
* await workerService.run(
|
|
67
|
+
* 'ImageService',
|
|
68
|
+
* 'generateThumbnail',
|
|
69
|
+
* [1920, 1080],
|
|
70
|
+
* {
|
|
71
|
+
* priority: 'HIGH',
|
|
72
|
+
* timeout: 3000,
|
|
73
|
+
* },
|
|
74
|
+
* );
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
run<T = unknown>(serviceName: string, methodName: string, args?: unknown[], overrides?: {
|
|
78
|
+
priority?: TaskPriority;
|
|
79
|
+
timeout?: number;
|
|
80
|
+
}): Promise<T>;
|
|
81
|
+
/**
|
|
82
|
+
* Gracefully shuts down worker pool.
|
|
83
|
+
*
|
|
84
|
+
* Called automatically by NestJS
|
|
85
|
+
* during application shutdown.
|
|
86
|
+
*/
|
|
87
|
+
onModuleDestroy(): Promise<void>;
|
|
88
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
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
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.WorkerService = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const worker_pool_1 = require("./worker.pool");
|
|
18
|
+
const discovery_service_1 = require("../discovery/discovery.service");
|
|
19
|
+
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 {
|
|
55
|
+
discovery;
|
|
56
|
+
options;
|
|
57
|
+
pool = null;
|
|
58
|
+
taskOptions = new Map();
|
|
59
|
+
constructor(discovery, options) {
|
|
60
|
+
this.discovery = discovery;
|
|
61
|
+
this.options = options;
|
|
62
|
+
}
|
|
63
|
+
onModuleInit() {
|
|
64
|
+
this.initPool();
|
|
65
|
+
}
|
|
66
|
+
initPool() {
|
|
67
|
+
if (this.pool)
|
|
68
|
+
return;
|
|
69
|
+
const tasks = this.discovery.scan();
|
|
70
|
+
for (const task of tasks) {
|
|
71
|
+
this.taskOptions.set(`${task.serviceName}.${task.methodName}`, { priority: task.priority, timeout: task.timeout });
|
|
72
|
+
}
|
|
73
|
+
const serialized = (0, di_serializer_1.serializeForWorker)(tasks);
|
|
74
|
+
this.pool = new worker_pool_1.WorkerPool(serialized, this.options.poolSize);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Executes a worker task.
|
|
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.
|
|
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
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
run(serviceName, methodName, args = [], overrides = {}) {
|
|
108
|
+
const key = `${serviceName}.${methodName}`;
|
|
109
|
+
const defaults = this.taskOptions.get(key) ??
|
|
110
|
+
{ priority: 'NORMAL' };
|
|
111
|
+
return this.pool.execute({
|
|
112
|
+
serviceName,
|
|
113
|
+
methodName,
|
|
114
|
+
args,
|
|
115
|
+
priority: overrides.priority ?? defaults.priority,
|
|
116
|
+
timeout: overrides.timeout ?? defaults.timeout,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Gracefully shuts down worker pool.
|
|
121
|
+
*
|
|
122
|
+
* Called automatically by NestJS
|
|
123
|
+
* during application shutdown.
|
|
124
|
+
*/
|
|
125
|
+
async onModuleDestroy() {
|
|
126
|
+
await this.pool?.destroy();
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
exports.WorkerService = WorkerService;
|
|
130
|
+
exports.WorkerService = WorkerService = __decorate([
|
|
131
|
+
(0, common_1.Injectable)(),
|
|
132
|
+
__param(1, (0, common_1.Inject)('WORKER_OPTIONS')),
|
|
133
|
+
__metadata("design:paramtypes", [discovery_service_1.WorkerDiscoveryService, Object])
|
|
134
|
+
], WorkerService);
|
|
135
|
+
//# sourceMappingURL=worker.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.service.js","sourceRoot":"","sources":["../../src/core/worker.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAmF;AACnF,+CAAyC;AACzC,sEAAsE;AACtE,uDAAuD;AAGvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEI,IAAM,aAAa,GAAnB,MAAM,aAAa;IAWL;IAEA;IAZX,IAAI,GAAsB,IAAI,CAAC;IACtB,WAAW,GAAG,IAAI,GAAG,EAMnC,CAAC;IAEJ,YACmB,SAAiC,EAEjC,OAA4B;QAF5B,cAAS,GAAT,SAAS,CAAwB;QAEjC,YAAO,GAAP,OAAO,CAAqB;IAE/C,CAAC;IAED,YAAY;QACV,IAAI,CAAC,QAAQ,EAAE,CAAC;IAClB,CAAC;IAEO,QAAQ;QACd,IAAI,IAAI,CAAC,IAAI;YAAE,OAAO;QAEtB,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;QAEpC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,WAAW,CAAC,GAAG,CAClB,GAAG,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,UAAU,EAAE,EACxC,EAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAC,CACjD,CAAC;QACJ,CAAC;QAED,MAAM,UAAU,GAAG,IAAA,kCAAkB,EAAC,KAAK,CAAC,CAAC;QAE7C,IAAI,CAAC,IAAI,GAAG,IAAI,wBAAU,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAChE,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACH,GAAG,CACD,WAAmB,EACnB,UAAkB,EAClB,OAAkB,EAAE,EACpB,YAA2D,EAAE;QAE7D,MAAM,GAAG,GAAG,GAAG,WAAW,IAAI,UAAU,EAAE,CAAC;QAE3C,MAAM,QAAQ,GACZ,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC;YACzB,EAAC,QAAQ,EAAE,QAAwB,EAAC,CAAC;QAEvC,OAAO,IAAI,CAAC,IAAK,CAAC,OAAO,CAAI;YAC3B,WAAW;YACX,UAAU;YACV,IAAI;YACJ,QAAQ,EAAE,SAAS,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ;YACjD,OAAO,EAAE,SAAS,CAAC,OAAO,IAAI,QAAQ,CAAC,OAAO;SAC/C,CAAC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,eAAe;QACnB,MAAM,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC;IAC7B,CAAC;CACF,CAAA;AAnGY,sCAAa;wBAAb,aAAa;IADzB,IAAA,mBAAU,GAAE;IAaR,WAAA,IAAA,eAAM,EAAC,gBAAgB,CAAC,CAAA;qCADG,0CAAsB;GAXzC,aAAa,CAmGzB"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
export declare const WORKER_CLASS_META = "worker:class";
|
|
3
|
+
export declare const WORKER_METHOD_META = "worker:method";
|
|
4
|
+
export declare const WORKER_DEPS_META = "worker:deps";
|
|
5
|
+
export interface WorkerTaskOptions {
|
|
6
|
+
priority?: 'HIGH' | 'NORMAL' | 'LOW';
|
|
7
|
+
timeout?: number;
|
|
8
|
+
}
|
|
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
|
+
export declare function WorkerClass(options?: {
|
|
24
|
+
deps?: (abstract new (...a: unknown[]) => unknown)[];
|
|
25
|
+
}): 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
|
+
export declare function WorkerTask(options?: WorkerTaskOptions): MethodDecorator;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WORKER_DEPS_META = exports.WORKER_METHOD_META = exports.WORKER_CLASS_META = void 0;
|
|
4
|
+
exports.WorkerClass = WorkerClass;
|
|
5
|
+
exports.WorkerTask = WorkerTask;
|
|
6
|
+
require("reflect-metadata");
|
|
7
|
+
exports.WORKER_CLASS_META = 'worker:class';
|
|
8
|
+
exports.WORKER_METHOD_META = 'worker:method';
|
|
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
|
+
*/
|
|
24
|
+
function WorkerClass(options = {}) {
|
|
25
|
+
return (target) => {
|
|
26
|
+
Reflect.defineMetadata(exports.WORKER_CLASS_META, true, target);
|
|
27
|
+
if (options.deps?.length) {
|
|
28
|
+
Reflect.defineMetadata(exports.WORKER_DEPS_META, options.deps, target);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
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
|
+
function WorkerTask(options = {}) {
|
|
44
|
+
return (target, propertyKey) => {
|
|
45
|
+
Reflect.defineMetadata(exports.WORKER_METHOD_META, options, target, propertyKey);
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=worker-task.decorator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-task.decorator.js","sourceRoot":"","sources":["../../src/decorators/worker-task.decorator.ts"],"names":[],"mappings":";;;AAyBA,kCASC;AAaD,gCAIC;AAnDD,4BAA0B;AAEb,QAAA,iBAAiB,GAAG,cAAc,CAAC;AACnC,QAAA,kBAAkB,GAAG,eAAe,CAAC;AACrC,QAAA,gBAAgB,GAAG,aAAa,CAAC;AAO9C;;;;;;;;;;;;;GAaG;AACH,SAAgB,WAAW,CACzB,UAAoE,EAAE;IAEtE,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;IACH,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { DiscoveredTask } from '../core/worker.interfaces';
|
|
2
|
+
import type { SerializedService } from './worker-container';
|
|
3
|
+
/**
|
|
4
|
+
* serializeForWorker – converts the live NestJS service graph into a
|
|
5
|
+
* structured-clone-safe SerializedService[] payload for workerData.
|
|
6
|
+
*
|
|
7
|
+
* KEY DESIGN DECISION: file paths instead of class source strings.
|
|
8
|
+
*
|
|
9
|
+
* The previous approach extracted class source via Class.toString() and
|
|
10
|
+
* eval()'d it inside the worker. This broke on any import that TS compiled
|
|
11
|
+
* to a file-scoped alias (crypto_1, node_os_1, …) because those aliases
|
|
12
|
+
* don't exist inside a bare new Function() scope.
|
|
13
|
+
*
|
|
14
|
+
* The new approach sends the absolute path to each compiled .js file.
|
|
15
|
+
* WorkerContainer runs each file in a vm context with a custom require()
|
|
16
|
+
* that stubs NestJS packages (so decorator calls at file-eval time are
|
|
17
|
+
* silent no-ops) while letting all other imports resolve normally.
|
|
18
|
+
*/
|
|
19
|
+
export declare function serializeForWorker(tasks: DiscoveredTask[]): SerializedService[];
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.serializeForWorker = serializeForWorker;
|
|
4
|
+
const worker_task_decorator_1 = require("../decorators/worker-task.decorator");
|
|
5
|
+
/**
|
|
6
|
+
* serializeForWorker – converts the live NestJS service graph into a
|
|
7
|
+
* structured-clone-safe SerializedService[] payload for workerData.
|
|
8
|
+
*
|
|
9
|
+
* KEY DESIGN DECISION: file paths instead of class source strings.
|
|
10
|
+
*
|
|
11
|
+
* The previous approach extracted class source via Class.toString() and
|
|
12
|
+
* eval()'d it inside the worker. This broke on any import that TS compiled
|
|
13
|
+
* to a file-scoped alias (crypto_1, node_os_1, …) because those aliases
|
|
14
|
+
* don't exist inside a bare new Function() scope.
|
|
15
|
+
*
|
|
16
|
+
* The new approach sends the absolute path to each compiled .js file.
|
|
17
|
+
* WorkerContainer runs each file in a vm context with a custom require()
|
|
18
|
+
* that stubs NestJS packages (so decorator calls at file-eval time are
|
|
19
|
+
* silent no-ops) while letting all other imports resolve normally.
|
|
20
|
+
*/
|
|
21
|
+
function serializeForWorker(tasks) {
|
|
22
|
+
const byService = new Map();
|
|
23
|
+
for (const task of tasks) {
|
|
24
|
+
if (!byService.has(task.serviceName)) {
|
|
25
|
+
byService.set(task.serviceName, { representative: task, methods: [] });
|
|
26
|
+
}
|
|
27
|
+
byService.get(task.serviceName).methods.push({
|
|
28
|
+
methodName: task.methodName,
|
|
29
|
+
priority: task.priority,
|
|
30
|
+
timeout: task.timeout,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
const result = [];
|
|
34
|
+
for (const [serviceName, { representative, methods }] of byService) {
|
|
35
|
+
const { metatype, instance, deps } = representative;
|
|
36
|
+
const depTypes = Reflect.getMetadata(worker_task_decorator_1.WORKER_DEPS_META, metatype) ?? [];
|
|
37
|
+
const serializedDeps = depTypes.map((DepType, i) => {
|
|
38
|
+
const depInstance = deps[i];
|
|
39
|
+
const propertyKey = findDepPropertyKey(instance, depInstance) ?? camelCase(DepType.name);
|
|
40
|
+
return {
|
|
41
|
+
name: DepType.name,
|
|
42
|
+
filePath: findFilePath(DepType),
|
|
43
|
+
snapshot: snapshotInstance(depInstance),
|
|
44
|
+
propertyKey,
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
result.push({
|
|
48
|
+
name: serviceName,
|
|
49
|
+
filePath: findFilePath(metatype),
|
|
50
|
+
methods,
|
|
51
|
+
deps: serializedDeps,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Locate the compiled .js file for this constructor by scanning require.cache.
|
|
58
|
+
* NestJS loads all providers via require() so every user-defined class will
|
|
59
|
+
* be present in the cache at the time serializeForWorker() is called.
|
|
60
|
+
*/
|
|
61
|
+
function findFilePath(ctor) {
|
|
62
|
+
for (const [filePath, mod] of Object.entries(require.cache)) {
|
|
63
|
+
if (!mod?.exports)
|
|
64
|
+
continue;
|
|
65
|
+
for (const val of Object.values(mod.exports)) {
|
|
66
|
+
if (val === ctor)
|
|
67
|
+
return filePath;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
throw new Error(`nestworker: could not find compiled file for "${ctor.name}" in require.cache. ` +
|
|
71
|
+
`Ensure the class is exported from its module file and the project is ` +
|
|
72
|
+
`compiled (not running via ts-node) before starting.`);
|
|
73
|
+
}
|
|
74
|
+
function findDepPropertyKey(serviceInstance, depInstance) {
|
|
75
|
+
if (!serviceInstance || typeof serviceInstance !== 'object')
|
|
76
|
+
return undefined;
|
|
77
|
+
for (const key of Object.getOwnPropertyNames(serviceInstance)) {
|
|
78
|
+
if (serviceInstance[key] === depInstance)
|
|
79
|
+
return key;
|
|
80
|
+
}
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
function snapshotInstance(instance) {
|
|
84
|
+
if (!instance || typeof instance !== 'object')
|
|
85
|
+
return {};
|
|
86
|
+
const out = {};
|
|
87
|
+
for (const k of Object.getOwnPropertyNames(instance)) {
|
|
88
|
+
const v = instance[k];
|
|
89
|
+
if (typeof v === 'function' || typeof v === 'symbol')
|
|
90
|
+
continue;
|
|
91
|
+
try {
|
|
92
|
+
structuredClone(v);
|
|
93
|
+
out[k] = v;
|
|
94
|
+
}
|
|
95
|
+
catch { /* skip non-cloneable */ }
|
|
96
|
+
}
|
|
97
|
+
return out;
|
|
98
|
+
}
|
|
99
|
+
function camelCase(name) {
|
|
100
|
+
return name.charAt(0).toLowerCase() + name.slice(1);
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=di-serializer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"di-serializer.js","sourceRoot":"","sources":["../../src/di/di-serializer.ts"],"names":[],"mappings":"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"}
|