@zintrust/workers 0.1.27
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 +861 -0
- package/dist/AnomalyDetection.d.ts +102 -0
- package/dist/AnomalyDetection.js +321 -0
- package/dist/AutoScaler.d.ts +127 -0
- package/dist/AutoScaler.js +425 -0
- package/dist/BroadcastWorker.d.ts +21 -0
- package/dist/BroadcastWorker.js +24 -0
- package/dist/CanaryController.d.ts +103 -0
- package/dist/CanaryController.js +380 -0
- package/dist/ChaosEngineering.d.ts +79 -0
- package/dist/ChaosEngineering.js +216 -0
- package/dist/CircuitBreaker.d.ts +106 -0
- package/dist/CircuitBreaker.js +374 -0
- package/dist/ClusterLock.d.ts +90 -0
- package/dist/ClusterLock.js +385 -0
- package/dist/ComplianceManager.d.ts +177 -0
- package/dist/ComplianceManager.js +556 -0
- package/dist/DatacenterOrchestrator.d.ts +133 -0
- package/dist/DatacenterOrchestrator.js +404 -0
- package/dist/DeadLetterQueue.d.ts +122 -0
- package/dist/DeadLetterQueue.js +539 -0
- package/dist/HealthMonitor.d.ts +42 -0
- package/dist/HealthMonitor.js +301 -0
- package/dist/MultiQueueWorker.d.ts +89 -0
- package/dist/MultiQueueWorker.js +277 -0
- package/dist/NotificationWorker.d.ts +21 -0
- package/dist/NotificationWorker.js +23 -0
- package/dist/Observability.d.ts +153 -0
- package/dist/Observability.js +530 -0
- package/dist/PluginManager.d.ts +123 -0
- package/dist/PluginManager.js +392 -0
- package/dist/PriorityQueue.d.ts +117 -0
- package/dist/PriorityQueue.js +244 -0
- package/dist/ResourceMonitor.d.ts +164 -0
- package/dist/ResourceMonitor.js +605 -0
- package/dist/SLAMonitor.d.ts +110 -0
- package/dist/SLAMonitor.js +274 -0
- package/dist/WorkerFactory.d.ts +193 -0
- package/dist/WorkerFactory.js +1507 -0
- package/dist/WorkerInit.d.ts +85 -0
- package/dist/WorkerInit.js +223 -0
- package/dist/WorkerMetrics.d.ts +114 -0
- package/dist/WorkerMetrics.js +509 -0
- package/dist/WorkerRegistry.d.ts +145 -0
- package/dist/WorkerRegistry.js +319 -0
- package/dist/WorkerShutdown.d.ts +61 -0
- package/dist/WorkerShutdown.js +159 -0
- package/dist/WorkerVersioning.d.ts +107 -0
- package/dist/WorkerVersioning.js +300 -0
- package/dist/build-manifest.json +462 -0
- package/dist/config/workerConfig.d.ts +3 -0
- package/dist/config/workerConfig.js +19 -0
- package/dist/createQueueWorker.d.ts +23 -0
- package/dist/createQueueWorker.js +113 -0
- package/dist/dashboard/index.d.ts +1 -0
- package/dist/dashboard/index.js +1 -0
- package/dist/dashboard/types.d.ts +117 -0
- package/dist/dashboard/types.js +1 -0
- package/dist/dashboard/workers-api.d.ts +4 -0
- package/dist/dashboard/workers-api.js +638 -0
- package/dist/dashboard/workers-dashboard-ui.d.ts +3 -0
- package/dist/dashboard/workers-dashboard-ui.js +1026 -0
- package/dist/dashboard/workers-dashboard.d.ts +4 -0
- package/dist/dashboard/workers-dashboard.js +904 -0
- package/dist/helper/index.d.ts +5 -0
- package/dist/helper/index.js +10 -0
- package/dist/http/WorkerApiController.d.ts +38 -0
- package/dist/http/WorkerApiController.js +312 -0
- package/dist/http/WorkerController.d.ts +374 -0
- package/dist/http/WorkerController.js +1351 -0
- package/dist/http/middleware/CustomValidation.d.ts +92 -0
- package/dist/http/middleware/CustomValidation.js +270 -0
- package/dist/http/middleware/DatacenterValidator.d.ts +3 -0
- package/dist/http/middleware/DatacenterValidator.js +94 -0
- package/dist/http/middleware/EditWorkerValidation.d.ts +7 -0
- package/dist/http/middleware/EditWorkerValidation.js +55 -0
- package/dist/http/middleware/FeaturesValidator.d.ts +3 -0
- package/dist/http/middleware/FeaturesValidator.js +60 -0
- package/dist/http/middleware/InfrastructureValidator.d.ts +31 -0
- package/dist/http/middleware/InfrastructureValidator.js +226 -0
- package/dist/http/middleware/OptionsValidator.d.ts +3 -0
- package/dist/http/middleware/OptionsValidator.js +112 -0
- package/dist/http/middleware/PayloadSanitizer.d.ts +7 -0
- package/dist/http/middleware/PayloadSanitizer.js +42 -0
- package/dist/http/middleware/ProcessorPathSanitizer.d.ts +3 -0
- package/dist/http/middleware/ProcessorPathSanitizer.js +74 -0
- package/dist/http/middleware/QueueNameSanitizer.d.ts +3 -0
- package/dist/http/middleware/QueueNameSanitizer.js +45 -0
- package/dist/http/middleware/ValidateDriver.d.ts +7 -0
- package/dist/http/middleware/ValidateDriver.js +20 -0
- package/dist/http/middleware/VersionSanitizer.d.ts +3 -0
- package/dist/http/middleware/VersionSanitizer.js +25 -0
- package/dist/http/middleware/WorkerNameSanitizer.d.ts +3 -0
- package/dist/http/middleware/WorkerNameSanitizer.js +46 -0
- package/dist/http/middleware/WorkerValidationChain.d.ts +27 -0
- package/dist/http/middleware/WorkerValidationChain.js +185 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.js +48 -0
- package/dist/routes/workers.d.ts +12 -0
- package/dist/routes/workers.js +81 -0
- package/dist/storage/WorkerStore.d.ts +45 -0
- package/dist/storage/WorkerStore.js +195 -0
- package/dist/type.d.ts +76 -0
- package/dist/type.js +1 -0
- package/dist/ui/router/ui.d.ts +3 -0
- package/dist/ui/router/ui.js +83 -0
- package/dist/ui/types/worker-ui.d.ts +229 -0
- package/dist/ui/types/worker-ui.js +5 -0
- package/package.json +53 -0
- package/src/AnomalyDetection.ts +434 -0
- package/src/AutoScaler.ts +654 -0
- package/src/BroadcastWorker.ts +34 -0
- package/src/CanaryController.ts +531 -0
- package/src/ChaosEngineering.ts +301 -0
- package/src/CircuitBreaker.ts +495 -0
- package/src/ClusterLock.ts +499 -0
- package/src/ComplianceManager.ts +815 -0
- package/src/DatacenterOrchestrator.ts +561 -0
- package/src/DeadLetterQueue.ts +733 -0
- package/src/HealthMonitor.ts +390 -0
- package/src/MultiQueueWorker.ts +431 -0
- package/src/NotificationWorker.ts +33 -0
- package/src/Observability.ts +696 -0
- package/src/PluginManager.ts +551 -0
- package/src/PriorityQueue.ts +351 -0
- package/src/ResourceMonitor.ts +769 -0
- package/src/SLAMonitor.ts +408 -0
- package/src/WorkerFactory.ts +2108 -0
- package/src/WorkerInit.ts +313 -0
- package/src/WorkerMetrics.ts +709 -0
- package/src/WorkerRegistry.ts +443 -0
- package/src/WorkerShutdown.ts +210 -0
- package/src/WorkerVersioning.ts +422 -0
- package/src/config/workerConfig.ts +25 -0
- package/src/createQueueWorker.ts +174 -0
- package/src/dashboard/index.ts +6 -0
- package/src/dashboard/types.ts +141 -0
- package/src/dashboard/workers-api.ts +785 -0
- package/src/dashboard/zintrust.svg +30 -0
- package/src/helper/index.ts +11 -0
- package/src/http/WorkerApiController.ts +369 -0
- package/src/http/WorkerController.ts +1512 -0
- package/src/http/middleware/CustomValidation.ts +360 -0
- package/src/http/middleware/DatacenterValidator.ts +124 -0
- package/src/http/middleware/EditWorkerValidation.ts +74 -0
- package/src/http/middleware/FeaturesValidator.ts +82 -0
- package/src/http/middleware/InfrastructureValidator.ts +295 -0
- package/src/http/middleware/OptionsValidator.ts +144 -0
- package/src/http/middleware/PayloadSanitizer.ts +52 -0
- package/src/http/middleware/ProcessorPathSanitizer.ts +86 -0
- package/src/http/middleware/QueueNameSanitizer.ts +55 -0
- package/src/http/middleware/ValidateDriver.ts +29 -0
- package/src/http/middleware/VersionSanitizer.ts +30 -0
- package/src/http/middleware/WorkerNameSanitizer.ts +56 -0
- package/src/http/middleware/WorkerValidationChain.ts +230 -0
- package/src/index.ts +98 -0
- package/src/routes/workers.ts +154 -0
- package/src/storage/WorkerStore.ts +240 -0
- package/src/type.ts +89 -0
- package/src/types/queue-monitor.d.ts +38 -0
- package/src/types/queue-redis.d.ts +38 -0
- package/src/ui/README.md +13 -0
- package/src/ui/components/JsonEditor.js +670 -0
- package/src/ui/components/JsonViewer.js +387 -0
- package/src/ui/components/WorkerCard.js +178 -0
- package/src/ui/components/WorkerExpandPanel.js +257 -0
- package/src/ui/components/fetcher.js +42 -0
- package/src/ui/components/sla-scorecard.js +32 -0
- package/src/ui/components/styles.css +30 -0
- package/src/ui/components/table-expander.js +34 -0
- package/src/ui/integration/worker-ui-integration.js +565 -0
- package/src/ui/router/ui.ts +99 -0
- package/src/ui/services/workerApi.js +240 -0
- package/src/ui/types/worker-ui.ts +283 -0
- package/src/ui/utils/jsonValidator.js +444 -0
- package/src/ui/workers/index.html +202 -0
- package/src/ui/workers/main.js +1781 -0
- package/src/ui/workers/styles.css +1350 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker Registry
|
|
3
|
+
* Central registry for all background workers with lifecycle management
|
|
4
|
+
* Sealed namespace for immutability
|
|
5
|
+
*/
|
|
6
|
+
import { ErrorFactory, Logger } from '@zintrust/core';
|
|
7
|
+
// Internal storage
|
|
8
|
+
const workers = new Map();
|
|
9
|
+
const registrations = new Map();
|
|
10
|
+
/**
|
|
11
|
+
* Helper: Calculate uptime in seconds
|
|
12
|
+
*/
|
|
13
|
+
const calculateUptime = (startedAt) => {
|
|
14
|
+
if (!startedAt)
|
|
15
|
+
return null;
|
|
16
|
+
return Math.floor((Date.now() - startedAt.getTime()) / 1000);
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Helper: Validate worker name
|
|
20
|
+
*/
|
|
21
|
+
const validateWorkerName = (name) => {
|
|
22
|
+
if (!name || typeof name !== 'string') {
|
|
23
|
+
throw ErrorFactory.createWorkerError('Worker name must be a non-empty string');
|
|
24
|
+
}
|
|
25
|
+
if (!/^[a-z0-9-]+$/.test(name)) {
|
|
26
|
+
throw ErrorFactory.createWorkerError('Worker name must contain only lowercase letters, numbers, and hyphens');
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Worker Registry - Sealed namespace
|
|
31
|
+
*/
|
|
32
|
+
export const WorkerRegistry = Object.freeze({
|
|
33
|
+
/**
|
|
34
|
+
* Register a worker with the registry
|
|
35
|
+
*/
|
|
36
|
+
register(options) {
|
|
37
|
+
validateWorkerName(options.name);
|
|
38
|
+
if (registrations.has(options.name)) {
|
|
39
|
+
Logger.warn(`Worker "${options.name}" is already registered. Skipping.`);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
registrations.set(options.name, options);
|
|
43
|
+
Logger.info(`Worker "${options.name}" registered successfully`);
|
|
44
|
+
},
|
|
45
|
+
/**
|
|
46
|
+
* Start a worker
|
|
47
|
+
*/
|
|
48
|
+
async start(name, version) {
|
|
49
|
+
validateWorkerName(name);
|
|
50
|
+
const registration = registrations.get(name);
|
|
51
|
+
if (!registration) {
|
|
52
|
+
throw ErrorFactory.createWorkerError(`Worker "${name}" is not registered`);
|
|
53
|
+
}
|
|
54
|
+
if (workers.has(name)) {
|
|
55
|
+
const existing = workers.get(name);
|
|
56
|
+
if (existing?.metadata.status === 'running') {
|
|
57
|
+
Logger.warn(`Worker "${name}" is already running`);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const versionSuffix = version === undefined ? '' : ` version ${version}`;
|
|
62
|
+
Logger.info(`Starting worker "${name}"${versionSuffix}...`);
|
|
63
|
+
try {
|
|
64
|
+
const instance = await registration.factory();
|
|
65
|
+
instance.metadata.status = 'starting';
|
|
66
|
+
instance.metadata.version = version ?? '1.0.0';
|
|
67
|
+
workers.set(name, instance);
|
|
68
|
+
instance.start();
|
|
69
|
+
instance.metadata.status = 'running';
|
|
70
|
+
instance.metadata.startedAt = new Date();
|
|
71
|
+
instance.metadata.stoppedAt = null;
|
|
72
|
+
Logger.info(`Worker "${name}" started successfully`);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
Logger.error(`Failed to start worker "${name}"`, error);
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
/**
|
|
80
|
+
* Stop a worker
|
|
81
|
+
*/
|
|
82
|
+
async stop(name) {
|
|
83
|
+
validateWorkerName(name);
|
|
84
|
+
const instance = workers.get(name);
|
|
85
|
+
if (!instance) {
|
|
86
|
+
Logger.warn(`Worker "${name}" is not running`);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (instance.metadata.status === 'stopped') {
|
|
90
|
+
Logger.warn(`Worker "${name}" is already stopped`);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
Logger.info(`Stopping worker "${name}"...`);
|
|
94
|
+
try {
|
|
95
|
+
instance.metadata.status = 'stopping';
|
|
96
|
+
await instance.stop();
|
|
97
|
+
instance.metadata.status = 'stopped';
|
|
98
|
+
instance.metadata.stoppedAt = new Date();
|
|
99
|
+
Logger.info(`Worker "${name}" stopped successfully`);
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
Logger.error(`Failed to stop worker "${name}"`, error);
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
/**
|
|
107
|
+
* Restart a worker (stop + start)
|
|
108
|
+
*/
|
|
109
|
+
async restart(name) {
|
|
110
|
+
validateWorkerName(name);
|
|
111
|
+
const instance = workers.get(name);
|
|
112
|
+
if (instance) {
|
|
113
|
+
await WorkerRegistry.stop(name);
|
|
114
|
+
instance.metadata.restartCount += 1;
|
|
115
|
+
}
|
|
116
|
+
await WorkerRegistry.start(name);
|
|
117
|
+
Logger.info(`Worker "${name}" restarted successfully`);
|
|
118
|
+
},
|
|
119
|
+
/**
|
|
120
|
+
* Sleep a worker (pause processing but keep lock)
|
|
121
|
+
*/
|
|
122
|
+
async sleep(name) {
|
|
123
|
+
validateWorkerName(name);
|
|
124
|
+
const instance = workers.get(name);
|
|
125
|
+
if (!instance) {
|
|
126
|
+
throw ErrorFactory.createWorkerError(`Worker "${name}" is not running`);
|
|
127
|
+
}
|
|
128
|
+
if (instance.metadata.status === 'sleeping') {
|
|
129
|
+
Logger.warn(`Worker "${name}" is already sleeping`);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
Logger.info(`Putting worker "${name}" to sleep...`);
|
|
133
|
+
try {
|
|
134
|
+
await instance.sleep();
|
|
135
|
+
instance.metadata.status = 'sleeping';
|
|
136
|
+
Logger.info(`Worker "${name}" is now sleeping`);
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
Logger.error(`Failed to sleep worker "${name}"`, error);
|
|
140
|
+
throw error;
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
/**
|
|
144
|
+
* Wakeup a worker (resume from sleep)
|
|
145
|
+
*/
|
|
146
|
+
async wakeup(name) {
|
|
147
|
+
validateWorkerName(name);
|
|
148
|
+
const instance = workers.get(name);
|
|
149
|
+
if (!instance) {
|
|
150
|
+
throw ErrorFactory.createWorkerError(`Worker "${name}" is not found`);
|
|
151
|
+
}
|
|
152
|
+
if (instance.metadata.status !== 'sleeping') {
|
|
153
|
+
Logger.warn(`Worker "${name}" is not sleeping (status: ${instance.metadata.status})`);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
Logger.info(`Waking up worker "${name}"...`);
|
|
157
|
+
try {
|
|
158
|
+
instance.wakeup();
|
|
159
|
+
instance.metadata.status = 'running';
|
|
160
|
+
Logger.info(`Worker "${name}" is now awake and running`);
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
Logger.error(`Failed to wake up worker "${name}"`, error);
|
|
164
|
+
throw error;
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
/**
|
|
168
|
+
* Get worker status
|
|
169
|
+
*/
|
|
170
|
+
status(name) {
|
|
171
|
+
validateWorkerName(name);
|
|
172
|
+
const instance = workers.get(name);
|
|
173
|
+
if (!instance) {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
return { ...instance.metadata };
|
|
177
|
+
},
|
|
178
|
+
/**
|
|
179
|
+
* List all registered workers
|
|
180
|
+
*/
|
|
181
|
+
list() {
|
|
182
|
+
return Array.from(registrations.keys());
|
|
183
|
+
},
|
|
184
|
+
/**
|
|
185
|
+
* List all running workers
|
|
186
|
+
*/
|
|
187
|
+
listRunning() {
|
|
188
|
+
const running = [];
|
|
189
|
+
for (const [name, instance] of workers.entries()) {
|
|
190
|
+
if (instance.metadata.status === 'running') {
|
|
191
|
+
running.push(name);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return running;
|
|
195
|
+
},
|
|
196
|
+
/**
|
|
197
|
+
* Stop all running workers
|
|
198
|
+
*/
|
|
199
|
+
async stopAll() {
|
|
200
|
+
Logger.info('Stopping all running workers...');
|
|
201
|
+
const running = WorkerRegistry.listRunning();
|
|
202
|
+
const tasks = running.map(async (name) => WorkerRegistry.stop(name));
|
|
203
|
+
try {
|
|
204
|
+
await Promise.all(tasks);
|
|
205
|
+
Logger.info(`Stopped ${running.length} workers successfully`);
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
Logger.error('Failed to stop some workers', error);
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
/**
|
|
213
|
+
* Get worker metrics
|
|
214
|
+
*/
|
|
215
|
+
getMetrics(name) {
|
|
216
|
+
validateWorkerName(name);
|
|
217
|
+
const instance = workers.get(name);
|
|
218
|
+
if (!instance) {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
processedCount: instance.metadata.processedCount,
|
|
223
|
+
errorCount: instance.metadata.errorCount,
|
|
224
|
+
memoryUsage: instance.metadata.memoryUsage,
|
|
225
|
+
cpuUsage: instance.metadata.cpuUsage,
|
|
226
|
+
};
|
|
227
|
+
},
|
|
228
|
+
/**
|
|
229
|
+
* Get worker health status
|
|
230
|
+
*/
|
|
231
|
+
getHealth(name) {
|
|
232
|
+
validateWorkerName(name);
|
|
233
|
+
const instance = workers.get(name);
|
|
234
|
+
if (!instance) {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
return instance.getHealth();
|
|
238
|
+
},
|
|
239
|
+
/**
|
|
240
|
+
* Get registry snapshot
|
|
241
|
+
*/
|
|
242
|
+
getSnapshot() {
|
|
243
|
+
const allWorkers = Array.from(workers.entries()).map(([name, instance]) => ({
|
|
244
|
+
name,
|
|
245
|
+
status: instance.metadata.status,
|
|
246
|
+
health: instance.getHealth(),
|
|
247
|
+
uptime: calculateUptime(instance.metadata.startedAt),
|
|
248
|
+
processedCount: instance.metadata.processedCount,
|
|
249
|
+
errorCount: instance.metadata.errorCount,
|
|
250
|
+
}));
|
|
251
|
+
const statusCounts = allWorkers.reduce((acc, w) => {
|
|
252
|
+
if (w.status === 'running')
|
|
253
|
+
acc.running++;
|
|
254
|
+
else if (w.status === 'stopped')
|
|
255
|
+
acc.stopped++;
|
|
256
|
+
else if (w.status === 'sleeping')
|
|
257
|
+
acc.sleeping++;
|
|
258
|
+
if (w.health === 'red')
|
|
259
|
+
acc.unhealthy++;
|
|
260
|
+
return acc;
|
|
261
|
+
}, { running: 0, stopped: 0, sleeping: 0, unhealthy: 0 });
|
|
262
|
+
return {
|
|
263
|
+
timestamp: new Date(),
|
|
264
|
+
totalWorkers: registrations.size,
|
|
265
|
+
runningWorkers: statusCounts.running,
|
|
266
|
+
stoppedWorkers: statusCounts.stopped,
|
|
267
|
+
sleepingWorkers: statusCounts.sleeping,
|
|
268
|
+
unhealthyWorkers: statusCounts.unhealthy,
|
|
269
|
+
workers: allWorkers,
|
|
270
|
+
};
|
|
271
|
+
},
|
|
272
|
+
/**
|
|
273
|
+
* Get worker topology (cluster view)
|
|
274
|
+
*/
|
|
275
|
+
getTopology() {
|
|
276
|
+
const topology = {};
|
|
277
|
+
for (const [name, instance] of workers.entries()) {
|
|
278
|
+
const region = instance.metadata.region;
|
|
279
|
+
if (topology[region].count <= 0) {
|
|
280
|
+
topology[region] = { workers: [], count: 0 };
|
|
281
|
+
}
|
|
282
|
+
topology[region].workers.push(name);
|
|
283
|
+
topology[region].count++;
|
|
284
|
+
}
|
|
285
|
+
return topology;
|
|
286
|
+
},
|
|
287
|
+
/**
|
|
288
|
+
* Unregister a worker and clear its instance
|
|
289
|
+
*/
|
|
290
|
+
unregister(name) {
|
|
291
|
+
validateWorkerName(name);
|
|
292
|
+
const instance = workers.get(name);
|
|
293
|
+
if (instance?.metadata.status === 'running') {
|
|
294
|
+
Logger.warn(`Worker "${name}" is still running during unregister`);
|
|
295
|
+
}
|
|
296
|
+
workers.delete(name);
|
|
297
|
+
registrations.delete(name);
|
|
298
|
+
Logger.info(`Worker "${name}" unregistered`);
|
|
299
|
+
},
|
|
300
|
+
/**
|
|
301
|
+
* Check if worker is registered
|
|
302
|
+
*/
|
|
303
|
+
isRegistered(name) {
|
|
304
|
+
return registrations.has(name);
|
|
305
|
+
},
|
|
306
|
+
/**
|
|
307
|
+
* Check if worker is running
|
|
308
|
+
*/
|
|
309
|
+
isRunning(name) {
|
|
310
|
+
const instance = workers.get(name);
|
|
311
|
+
return instance?.metadata.status === 'running';
|
|
312
|
+
},
|
|
313
|
+
/**
|
|
314
|
+
* Get worker instance (internal use)
|
|
315
|
+
*/
|
|
316
|
+
getInstance(name) {
|
|
317
|
+
return workers.get(name) ?? null;
|
|
318
|
+
},
|
|
319
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker Shutdown Coordinator
|
|
3
|
+
*
|
|
4
|
+
* Centralized graceful shutdown handling for the worker management system.
|
|
5
|
+
* Coordinates orderly shutdown of all worker modules and the WorkerFactory.
|
|
6
|
+
*/
|
|
7
|
+
export interface IShutdownOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Timeout for graceful shutdown in milliseconds
|
|
10
|
+
*/
|
|
11
|
+
timeout?: number;
|
|
12
|
+
/**
|
|
13
|
+
* Whether to force exit after timeout
|
|
14
|
+
*/
|
|
15
|
+
forceExit?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Signal that triggered shutdown (SIGTERM, SIGINT, etc.)
|
|
18
|
+
*/
|
|
19
|
+
signal?: string;
|
|
20
|
+
}
|
|
21
|
+
interface IShutdownState {
|
|
22
|
+
isShuttingDown: boolean;
|
|
23
|
+
completedAt: Date | null;
|
|
24
|
+
startedAt: Date | null;
|
|
25
|
+
reason: string | null;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Perform graceful shutdown of all worker modules
|
|
29
|
+
*/
|
|
30
|
+
declare function shutdown(options?: IShutdownOptions): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Register process signal handlers for graceful shutdown
|
|
33
|
+
*/
|
|
34
|
+
declare function registerShutdownHandlers(): void;
|
|
35
|
+
/**
|
|
36
|
+
* Check if system is currently shutting down
|
|
37
|
+
*/
|
|
38
|
+
declare function isShuttingDown(): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Get current shutdown state
|
|
41
|
+
*/
|
|
42
|
+
declare function getShutdownState(): Readonly<IShutdownState>;
|
|
43
|
+
export declare const WorkerShutdown: Readonly<{
|
|
44
|
+
/**
|
|
45
|
+
* Perform graceful shutdown of all worker modules
|
|
46
|
+
*/
|
|
47
|
+
shutdown: typeof shutdown;
|
|
48
|
+
/**
|
|
49
|
+
* Register process signal handlers for graceful shutdown
|
|
50
|
+
*/
|
|
51
|
+
registerShutdownHandlers: typeof registerShutdownHandlers;
|
|
52
|
+
/**
|
|
53
|
+
* Check if system is currently shutting down
|
|
54
|
+
*/
|
|
55
|
+
isShuttingDown: typeof isShuttingDown;
|
|
56
|
+
/**
|
|
57
|
+
* Get current shutdown state
|
|
58
|
+
*/
|
|
59
|
+
getShutdownState: typeof getShutdownState;
|
|
60
|
+
}>;
|
|
61
|
+
export {};
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker Shutdown Coordinator
|
|
3
|
+
*
|
|
4
|
+
* Centralized graceful shutdown handling for the worker management system.
|
|
5
|
+
* Coordinates orderly shutdown of all worker modules and the WorkerFactory.
|
|
6
|
+
*/
|
|
7
|
+
import { Logger } from '@zintrust/core';
|
|
8
|
+
import { WorkerFactory } from './WorkerFactory';
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Implementation
|
|
11
|
+
// ============================================================================
|
|
12
|
+
const state = {
|
|
13
|
+
isShuttingDown: false,
|
|
14
|
+
completedAt: null,
|
|
15
|
+
startedAt: null,
|
|
16
|
+
reason: null,
|
|
17
|
+
};
|
|
18
|
+
let shutdownHandlersRegistered = false;
|
|
19
|
+
/**
|
|
20
|
+
* Perform graceful shutdown of all worker modules
|
|
21
|
+
*/
|
|
22
|
+
async function shutdown(options = {}) {
|
|
23
|
+
const { timeout = 30000, forceExit = true, signal = 'unknown' } = options;
|
|
24
|
+
// Prevent concurrent shutdowns
|
|
25
|
+
if (state.isShuttingDown) {
|
|
26
|
+
Logger.warn('Shutdown already in progress, ignoring duplicate request');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
state.isShuttingDown = true;
|
|
30
|
+
state.startedAt = new Date();
|
|
31
|
+
state.reason = `Signal: ${signal}`;
|
|
32
|
+
Logger.info('🛑 Initiating graceful shutdown of worker management system', {
|
|
33
|
+
signal,
|
|
34
|
+
timeout,
|
|
35
|
+
forceExit,
|
|
36
|
+
});
|
|
37
|
+
// Setup timeout for forced shutdown
|
|
38
|
+
let timeoutHandle = null;
|
|
39
|
+
if (forceExit && timeout > 0) {
|
|
40
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
41
|
+
timeoutHandle = setTimeout(() => {
|
|
42
|
+
Logger.error('❌ Graceful shutdown timeout exceeded, forcing exit', { timeout });
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}, timeout);
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
// Shutdown WorkerFactory - this will coordinate shutdown of all modules
|
|
48
|
+
await WorkerFactory.shutdown();
|
|
49
|
+
state.completedAt = new Date();
|
|
50
|
+
const duration = state.completedAt.getTime() - (state.startedAt?.getTime() ?? 0);
|
|
51
|
+
Logger.info('✅ Worker management system shutdown complete', {
|
|
52
|
+
duration: `${duration}ms`,
|
|
53
|
+
signal,
|
|
54
|
+
});
|
|
55
|
+
// Clear timeout if successful
|
|
56
|
+
if (timeoutHandle) {
|
|
57
|
+
clearTimeout(timeoutHandle);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
Logger.error('❌ Error during worker management system shutdown', error);
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Register process signal handlers for graceful shutdown
|
|
67
|
+
*/
|
|
68
|
+
function registerShutdownHandlers() {
|
|
69
|
+
if (shutdownHandlersRegistered) {
|
|
70
|
+
Logger.debug('Shutdown handlers already registered, skipping');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
Logger.debug('Registering worker management system shutdown handlers');
|
|
74
|
+
// SIGTERM - graceful shutdown (Docker, systemd, etc.)
|
|
75
|
+
process.on('SIGTERM', async () => {
|
|
76
|
+
Logger.info('📨 Received SIGTERM signal');
|
|
77
|
+
try {
|
|
78
|
+
await shutdown({ signal: 'SIGTERM', timeout: 30000, forceExit: true });
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
Logger.error('Error during SIGTERM shutdown', error);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
// SIGINT - user interrupt (Ctrl+C) - REMOVED: handled by bootstrap.ts to prevent race condition
|
|
85
|
+
// process.on('SIGINT', async () => {
|
|
86
|
+
// Logger.info('📨 Received SIGINT signal');
|
|
87
|
+
// try {
|
|
88
|
+
// await shutdown({ signal: 'SIGINT', timeout: 30000, forceExit: true });
|
|
89
|
+
// } catch (error) {
|
|
90
|
+
// Logger.error('Error during SIGINT shutdown', error);
|
|
91
|
+
// }
|
|
92
|
+
// });
|
|
93
|
+
// SIGHUP - terminal closed
|
|
94
|
+
process.on('SIGHUP', async () => {
|
|
95
|
+
Logger.info('📨 Received SIGHUP signal');
|
|
96
|
+
try {
|
|
97
|
+
await shutdown({ signal: 'SIGHUP', timeout: 30000, forceExit: true });
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
Logger.error('Error during SIGHUP shutdown', error);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
// Handle uncaught errors during shutdown
|
|
104
|
+
process.on('uncaughtException', async (error) => {
|
|
105
|
+
Logger.error('💥 Uncaught exception during worker operations', error);
|
|
106
|
+
try {
|
|
107
|
+
await shutdown({ signal: 'uncaughtException', timeout: 10000, forceExit: true });
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Ignore errors during emergency shutdown
|
|
111
|
+
}
|
|
112
|
+
process.exit(1);
|
|
113
|
+
});
|
|
114
|
+
process.on('unhandledRejection', async (reason) => {
|
|
115
|
+
Logger.error('💥 Unhandled promise rejection during worker operations', reason);
|
|
116
|
+
try {
|
|
117
|
+
await shutdown({ signal: 'unhandledRejection', timeout: 10000, forceExit: true });
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// Ignore errors during emergency shutdown
|
|
121
|
+
}
|
|
122
|
+
process.exit(1);
|
|
123
|
+
});
|
|
124
|
+
shutdownHandlersRegistered = true;
|
|
125
|
+
Logger.debug('Worker management system shutdown handlers registered');
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Check if system is currently shutting down
|
|
129
|
+
*/
|
|
130
|
+
function isShuttingDown() {
|
|
131
|
+
return state.isShuttingDown;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Get current shutdown state
|
|
135
|
+
*/
|
|
136
|
+
function getShutdownState() {
|
|
137
|
+
return { ...state };
|
|
138
|
+
}
|
|
139
|
+
// ============================================================================
|
|
140
|
+
// Public API (Sealed Namespace)
|
|
141
|
+
// ============================================================================
|
|
142
|
+
export const WorkerShutdown = Object.freeze({
|
|
143
|
+
/**
|
|
144
|
+
* Perform graceful shutdown of all worker modules
|
|
145
|
+
*/
|
|
146
|
+
shutdown,
|
|
147
|
+
/**
|
|
148
|
+
* Register process signal handlers for graceful shutdown
|
|
149
|
+
*/
|
|
150
|
+
registerShutdownHandlers,
|
|
151
|
+
/**
|
|
152
|
+
* Check if system is currently shutting down
|
|
153
|
+
*/
|
|
154
|
+
isShuttingDown,
|
|
155
|
+
/**
|
|
156
|
+
* Get current shutdown state
|
|
157
|
+
*/
|
|
158
|
+
getShutdownState,
|
|
159
|
+
});
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker Versioning System
|
|
3
|
+
* Semantic versioning support for workers with backward compatibility
|
|
4
|
+
* Sealed namespace for immutability
|
|
5
|
+
*/
|
|
6
|
+
export type SemanticVersion = {
|
|
7
|
+
major: number;
|
|
8
|
+
minor: number;
|
|
9
|
+
patch: number;
|
|
10
|
+
prerelease?: string;
|
|
11
|
+
build?: string;
|
|
12
|
+
};
|
|
13
|
+
export type WorkerVersion = {
|
|
14
|
+
workerName: string;
|
|
15
|
+
version: SemanticVersion;
|
|
16
|
+
createdAt: Date;
|
|
17
|
+
deprecatedAt?: Date;
|
|
18
|
+
eolDate?: Date;
|
|
19
|
+
isActive: boolean;
|
|
20
|
+
isDeprecated: boolean;
|
|
21
|
+
migrationPath?: string;
|
|
22
|
+
changelog?: string;
|
|
23
|
+
breakingChanges?: string[];
|
|
24
|
+
};
|
|
25
|
+
export type VersionCompatibility = {
|
|
26
|
+
sourceVersion: SemanticVersion;
|
|
27
|
+
targetVersion: SemanticVersion;
|
|
28
|
+
compatible: boolean;
|
|
29
|
+
requiresMigration: boolean;
|
|
30
|
+
breakingChanges: string[];
|
|
31
|
+
recommendations: string[];
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Worker Versioning - Sealed namespace
|
|
35
|
+
*/
|
|
36
|
+
export declare const WorkerVersioning: Readonly<{
|
|
37
|
+
/**
|
|
38
|
+
* Register a worker version
|
|
39
|
+
*/
|
|
40
|
+
register(workerVersion: Omit<WorkerVersion, "createdAt" | "isActive" | "isDeprecated">): void;
|
|
41
|
+
/**
|
|
42
|
+
* Get worker version
|
|
43
|
+
*/
|
|
44
|
+
getVersion(workerName: string, versionStr: string): WorkerVersion | null;
|
|
45
|
+
/**
|
|
46
|
+
* Get all versions for a worker
|
|
47
|
+
*/
|
|
48
|
+
getVersions(workerName: string, includeDeprecated?: boolean): ReadonlyArray<WorkerVersion>;
|
|
49
|
+
/**
|
|
50
|
+
* Get latest version
|
|
51
|
+
*/
|
|
52
|
+
getLatest(workerName: string): WorkerVersion | null;
|
|
53
|
+
/**
|
|
54
|
+
* Deprecate a version
|
|
55
|
+
*/
|
|
56
|
+
deprecate(workerName: string, versionStr: string, migrationPath?: string, eolDate?: Date): void;
|
|
57
|
+
/**
|
|
58
|
+
* Deactivate a version (stop accepting new jobs)
|
|
59
|
+
*/
|
|
60
|
+
deactivate(workerName: string, versionStr: string): void;
|
|
61
|
+
/**
|
|
62
|
+
* Activate a version
|
|
63
|
+
*/
|
|
64
|
+
activate(workerName: string, versionStr: string): void;
|
|
65
|
+
/**
|
|
66
|
+
* Check compatibility between versions
|
|
67
|
+
*/
|
|
68
|
+
checkCompatibility(workerName: string, sourceVersionStr: string, targetVersionStr: string): VersionCompatibility;
|
|
69
|
+
/**
|
|
70
|
+
* Set version alias
|
|
71
|
+
*/
|
|
72
|
+
setAlias(workerName: string, alias: string, versionStr: string): void;
|
|
73
|
+
/**
|
|
74
|
+
* Get version by alias
|
|
75
|
+
*/
|
|
76
|
+
resolveAlias(workerName: string, alias: string): string | null;
|
|
77
|
+
/**
|
|
78
|
+
* Parse version string
|
|
79
|
+
*/
|
|
80
|
+
parse(versionStr: string): SemanticVersion;
|
|
81
|
+
/**
|
|
82
|
+
* Convert version to string
|
|
83
|
+
*/
|
|
84
|
+
stringify(version: SemanticVersion): string;
|
|
85
|
+
/**
|
|
86
|
+
* Compare two versions
|
|
87
|
+
*/
|
|
88
|
+
compare(v1Str: string, v2Str: string): number;
|
|
89
|
+
/**
|
|
90
|
+
* Get version summary
|
|
91
|
+
*/
|
|
92
|
+
getSummary(workerName: string): {
|
|
93
|
+
totalVersions: number;
|
|
94
|
+
activeVersions: number;
|
|
95
|
+
deprecatedVersions: number;
|
|
96
|
+
latest: string | null;
|
|
97
|
+
stable: string | null;
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Clear all versions for a worker
|
|
101
|
+
*/
|
|
102
|
+
clear(workerName: string): void;
|
|
103
|
+
/**
|
|
104
|
+
* Shutdown
|
|
105
|
+
*/
|
|
106
|
+
shutdown(): void;
|
|
107
|
+
}>;
|