@engjts/nexus 0.1.8 → 0.1.10
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/dist/advanced/playground/playground.js.map +1 -1
- package/dist/advanced/static/generateDirectoryListing.d.ts +1 -1
- package/dist/advanced/static/generateDirectoryListing.d.ts.map +1 -1
- package/dist/advanced/static/generateDirectoryListing.js +12 -6
- package/dist/advanced/static/generateDirectoryListing.js.map +1 -1
- package/dist/advanced/static/index.d.ts +2 -0
- package/dist/advanced/static/index.d.ts.map +1 -1
- package/dist/advanced/static/index.js +4 -1
- package/dist/advanced/static/index.js.map +1 -1
- package/dist/advanced/static/serveStatic.d.ts.map +1 -1
- package/dist/advanced/static/serveStatic.js +7 -1
- package/dist/advanced/static/serveStatic.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/BENCHMARK_REPORT.md +0 -343
- package/documentation/01-getting-started.md +0 -240
- package/documentation/02-context.md +0 -335
- package/documentation/03-routing.md +0 -397
- package/documentation/04-middleware.md +0 -483
- package/documentation/05-validation.md +0 -514
- package/documentation/06-error-handling.md +0 -465
- package/documentation/07-performance.md +0 -364
- package/documentation/08-adapters.md +0 -470
- package/documentation/09-api-reference.md +0 -548
- package/documentation/10-examples.md +0 -582
- package/documentation/11-deployment.md +0 -477
- package/documentation/12-sentry.md +0 -620
- package/documentation/13-sentry-data-storage.md +0 -996
- package/documentation/14-sentry-data-reference.md +0 -457
- package/documentation/15-sentry-summary.md +0 -409
- package/documentation/16-alerts-system.md +0 -745
- package/documentation/17-alert-adapters.md +0 -696
- package/documentation/18-alerts-implementation-summary.md +0 -385
- package/documentation/19-class-based-routing.md +0 -840
- package/documentation/20-websocket-realtime.md +0 -813
- package/documentation/21-cache-system.md +0 -510
- package/documentation/22-job-queue.md +0 -772
- package/documentation/23-sentry-plugin.md +0 -551
- package/documentation/24-testing-utilities.md +0 -1287
- package/documentation/25-api-versioning.md +0 -533
- package/documentation/26-context-store.md +0 -607
- package/documentation/27-dependency-injection.md +0 -329
- package/documentation/28-lifecycle-hooks.md +0 -521
- package/documentation/29-package-structure.md +0 -196
- package/documentation/30-plugin-system.md +0 -414
- package/documentation/31-jwt-authentication.md +0 -597
- package/documentation/32-cli.md +0 -268
- package/documentation/ALERTS-COMPLETE-SUMMARY.md +0 -429
- package/documentation/ALERTS-INDEX.md +0 -330
- package/documentation/ALERTS-QUICK-REFERENCE.md +0 -286
- package/documentation/README.md +0 -178
- package/documentation/index.html +0 -34
- package/modern_framework_paper.md +0 -1870
- package/public/css/style.css +0 -87
- package/public/index.html +0 -34
- package/public/js/app.js +0 -27
- package/src/advanced/cache/InMemoryCacheStore.ts +0 -68
- package/src/advanced/cache/MultiTierCache.ts +0 -194
- package/src/advanced/cache/RedisCacheStore.ts +0 -341
- package/src/advanced/cache/index.ts +0 -5
- package/src/advanced/cache/types.ts +0 -40
- package/src/advanced/graphql/SimpleDataLoader.ts +0 -42
- package/src/advanced/graphql/index.ts +0 -22
- package/src/advanced/graphql/server.ts +0 -252
- package/src/advanced/graphql/types.ts +0 -42
- package/src/advanced/jobs/InMemoryQueueStore.ts +0 -68
- package/src/advanced/jobs/JobQueue.ts +0 -556
- package/src/advanced/jobs/RedisQueueStore.ts +0 -367
- package/src/advanced/jobs/index.ts +0 -5
- package/src/advanced/jobs/types.ts +0 -70
- package/src/advanced/observability/APMManager.ts +0 -163
- package/src/advanced/observability/AlertManager.ts +0 -109
- package/src/advanced/observability/MetricRegistry.ts +0 -151
- package/src/advanced/observability/ObservabilityCenter.ts +0 -304
- package/src/advanced/observability/StructuredLogger.ts +0 -154
- package/src/advanced/observability/TracingManager.ts +0 -117
- package/src/advanced/observability/adapters.ts +0 -304
- package/src/advanced/observability/createObservabilityMiddleware.ts +0 -63
- package/src/advanced/observability/index.ts +0 -11
- package/src/advanced/observability/types.ts +0 -174
- package/src/advanced/playground/extractPathParams.ts +0 -6
- package/src/advanced/playground/generateFieldExample.ts +0 -31
- package/src/advanced/playground/generatePlaygroundHTML.ts +0 -1956
- package/src/advanced/playground/generateSummary.ts +0 -19
- package/src/advanced/playground/getTagFromPath.ts +0 -9
- package/src/advanced/playground/index.ts +0 -8
- package/src/advanced/playground/playground.ts +0 -250
- package/src/advanced/playground/types.ts +0 -49
- package/src/advanced/playground/zodToExample.ts +0 -16
- package/src/advanced/playground/zodToParams.ts +0 -15
- package/src/advanced/postman/buildAuth.ts +0 -31
- package/src/advanced/postman/buildBody.ts +0 -15
- package/src/advanced/postman/buildQueryParams.ts +0 -27
- package/src/advanced/postman/buildRequestItem.ts +0 -36
- package/src/advanced/postman/buildResponses.ts +0 -11
- package/src/advanced/postman/buildUrl.ts +0 -33
- package/src/advanced/postman/capitalize.ts +0 -4
- package/src/advanced/postman/generateCollection.ts +0 -59
- package/src/advanced/postman/generateEnvironment.ts +0 -34
- package/src/advanced/postman/generateExampleFromZod.ts +0 -21
- package/src/advanced/postman/generateFieldExample.ts +0 -45
- package/src/advanced/postman/generateName.ts +0 -20
- package/src/advanced/postman/generateUUID.ts +0 -11
- package/src/advanced/postman/getTagFromPath.ts +0 -10
- package/src/advanced/postman/index.ts +0 -28
- package/src/advanced/postman/postman.ts +0 -156
- package/src/advanced/postman/slugify.ts +0 -7
- package/src/advanced/postman/types.ts +0 -140
- package/src/advanced/realtime/index.ts +0 -18
- package/src/advanced/realtime/websocket.ts +0 -231
- package/src/advanced/sentry/index.ts +0 -1236
- package/src/advanced/sentry/types.ts +0 -355
- package/src/advanced/static/generateDirectoryListing.ts +0 -47
- package/src/advanced/static/generateETag.ts +0 -7
- package/src/advanced/static/getMimeType.ts +0 -9
- package/src/advanced/static/index.ts +0 -32
- package/src/advanced/static/isSafePath.ts +0 -13
- package/src/advanced/static/publicDir.ts +0 -21
- package/src/advanced/static/serveStatic.ts +0 -225
- package/src/advanced/static/spa.ts +0 -24
- package/src/advanced/static/types.ts +0 -159
- package/src/advanced/swagger/SwaggerGenerator.ts +0 -66
- package/src/advanced/swagger/buildOperation.ts +0 -61
- package/src/advanced/swagger/buildParameters.ts +0 -61
- package/src/advanced/swagger/buildRequestBody.ts +0 -21
- package/src/advanced/swagger/buildResponses.ts +0 -54
- package/src/advanced/swagger/capitalize.ts +0 -5
- package/src/advanced/swagger/convertPath.ts +0 -9
- package/src/advanced/swagger/createSwagger.ts +0 -12
- package/src/advanced/swagger/generateOperationId.ts +0 -21
- package/src/advanced/swagger/generateSpec.ts +0 -105
- package/src/advanced/swagger/generateSummary.ts +0 -24
- package/src/advanced/swagger/generateSwaggerUI.ts +0 -70
- package/src/advanced/swagger/generateThemeCss.ts +0 -53
- package/src/advanced/swagger/index.ts +0 -25
- package/src/advanced/swagger/swagger.ts +0 -237
- package/src/advanced/swagger/types.ts +0 -206
- package/src/advanced/swagger/zodFieldToOpenAPI.ts +0 -94
- package/src/advanced/swagger/zodSchemaToOpenAPI.ts +0 -50
- package/src/advanced/swagger/zodToOpenAPI.ts +0 -22
- package/src/advanced/testing/factory.ts +0 -509
- package/src/advanced/testing/harness.ts +0 -612
- package/src/advanced/testing/index.ts +0 -430
- package/src/advanced/testing/load-test.ts +0 -618
- package/src/advanced/testing/mock-server.ts +0 -498
- package/src/advanced/testing/mock.ts +0 -670
- package/src/cli/bin.ts +0 -9
- package/src/cli/cli.ts +0 -158
- package/src/cli/commands/add.ts +0 -178
- package/src/cli/commands/build.ts +0 -73
- package/src/cli/commands/create.ts +0 -166
- package/src/cli/commands/dev.ts +0 -85
- package/src/cli/commands/generate.ts +0 -99
- package/src/cli/commands/help.ts +0 -95
- package/src/cli/commands/init.ts +0 -91
- package/src/cli/commands/version.ts +0 -38
- package/src/cli/index.ts +0 -6
- package/src/cli/templates/generators.ts +0 -359
- package/src/cli/templates/index.ts +0 -680
- package/src/cli/utils/exec.ts +0 -52
- package/src/cli/utils/file-system.ts +0 -78
- package/src/cli/utils/logger.ts +0 -111
- package/src/core/adapter.ts +0 -88
- package/src/core/application.ts +0 -1453
- package/src/core/context-pool.ts +0 -79
- package/src/core/context.ts +0 -856
- package/src/core/index.ts +0 -94
- package/src/core/middleware.ts +0 -272
- package/src/core/performance/buffer-pool.ts +0 -108
- package/src/core/performance/middleware-optimizer.ts +0 -162
- package/src/core/plugin/PluginManager.ts +0 -435
- package/src/core/plugin/builder.ts +0 -358
- package/src/core/plugin/index.ts +0 -50
- package/src/core/plugin/types.ts +0 -214
- package/src/core/router/file-router.ts +0 -623
- package/src/core/router/index.ts +0 -260
- package/src/core/router/radix-tree.ts +0 -242
- package/src/core/serializer.ts +0 -397
- package/src/core/store/index.ts +0 -30
- package/src/core/store/registry.ts +0 -178
- package/src/core/store/request-store.ts +0 -240
- package/src/core/store/types.ts +0 -233
- package/src/core/types.ts +0 -616
- package/src/database/adapter.ts +0 -35
- package/src/database/adapters/index.ts +0 -1
- package/src/database/adapters/mysql.ts +0 -669
- package/src/database/database.ts +0 -70
- package/src/database/dialect.ts +0 -388
- package/src/database/index.ts +0 -12
- package/src/database/migrations.ts +0 -86
- package/src/database/optimizer.ts +0 -125
- package/src/database/query-builder.ts +0 -404
- package/src/database/realtime.ts +0 -53
- package/src/database/schema.ts +0 -71
- package/src/database/transactions.ts +0 -56
- package/src/database/types.ts +0 -87
- package/src/deployment/cluster.ts +0 -471
- package/src/deployment/config.ts +0 -454
- package/src/deployment/docker.ts +0 -599
- package/src/deployment/graceful-shutdown.ts +0 -373
- package/src/deployment/index.ts +0 -56
- package/src/index.ts +0 -281
- package/src/security/adapter.ts +0 -318
- package/src/security/auth/JWTPlugin.ts +0 -234
- package/src/security/auth/JWTProvider.ts +0 -316
- package/src/security/auth/adapter.ts +0 -12
- package/src/security/auth/jwt.ts +0 -234
- package/src/security/auth/middleware.ts +0 -188
- package/src/security/csrf.ts +0 -220
- package/src/security/headers.ts +0 -108
- package/src/security/index.ts +0 -60
- package/src/security/rate-limit/adapter.ts +0 -7
- package/src/security/rate-limit/memory.ts +0 -108
- package/src/security/rate-limit/middleware.ts +0 -181
- package/src/security/sanitization.ts +0 -75
- package/src/security/types.ts +0 -240
- package/src/security/utils.ts +0 -52
- package/tsconfig.json +0 -39
|
@@ -1,471 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cluster Manager
|
|
3
|
-
* Provides multi-process clustering for high availability and performance
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import cluster, { Worker } from 'cluster';
|
|
7
|
-
import { cpus } from 'os';
|
|
8
|
-
import { EventEmitter } from 'events';
|
|
9
|
-
|
|
10
|
-
export interface ClusterOptions {
|
|
11
|
-
/**
|
|
12
|
-
* Number of worker processes
|
|
13
|
-
* @default 'auto' (number of CPU cores)
|
|
14
|
-
*/
|
|
15
|
-
workers?: number | 'auto';
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Restart strategy for workers
|
|
19
|
-
* @default 'rolling'
|
|
20
|
-
*/
|
|
21
|
-
restartStrategy?: 'rolling' | 'all-at-once' | 'none';
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Delay between worker restarts (ms)
|
|
25
|
-
* @default 5000
|
|
26
|
-
*/
|
|
27
|
-
restartDelay?: number;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Maximum restart attempts per worker
|
|
31
|
-
* @default 10
|
|
32
|
-
*/
|
|
33
|
-
maxRestarts?: number;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Time window for counting restarts (ms)
|
|
37
|
-
* @default 60000
|
|
38
|
-
*/
|
|
39
|
-
restartWindow?: number;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Callback when a worker starts
|
|
43
|
-
*/
|
|
44
|
-
onWorkerStart?: (worker: Worker) => void;
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Callback when a worker exits
|
|
48
|
-
*/
|
|
49
|
-
onWorkerExit?: (worker: Worker, code: number, signal: string) => void;
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Callback when a worker sends a message
|
|
53
|
-
*/
|
|
54
|
-
onWorkerMessage?: (worker: Worker, message: any) => void;
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Enable sticky sessions for WebSocket support
|
|
58
|
-
* @default false
|
|
59
|
-
*/
|
|
60
|
-
stickySessions?: boolean;
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Enable verbose logging
|
|
64
|
-
* @default false
|
|
65
|
-
*/
|
|
66
|
-
verbose?: boolean;
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Environment variables for workers
|
|
70
|
-
*/
|
|
71
|
-
env?: Record<string, string>;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export interface WorkerInfo {
|
|
75
|
-
id: number;
|
|
76
|
-
pid: number;
|
|
77
|
-
startTime: number;
|
|
78
|
-
restartCount: number;
|
|
79
|
-
lastRestartTime?: number;
|
|
80
|
-
status: 'starting' | 'online' | 'listening' | 'disconnecting' | 'dead';
|
|
81
|
-
memoryUsage?: NodeJS.MemoryUsage;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export interface ClusterStats {
|
|
85
|
-
workers: WorkerInfo[];
|
|
86
|
-
totalWorkers: number;
|
|
87
|
-
activeWorkers: number;
|
|
88
|
-
uptime: number;
|
|
89
|
-
totalRestarts: number;
|
|
90
|
-
isPrimary: boolean;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Cluster Manager for multi-process deployment
|
|
95
|
-
*/
|
|
96
|
-
export class ClusterManager extends EventEmitter {
|
|
97
|
-
private options: Required<ClusterOptions>;
|
|
98
|
-
private workerRestarts: Map<number, { count: number; timestamps: number[] }> = new Map();
|
|
99
|
-
private workerInfo: Map<number, WorkerInfo> = new Map();
|
|
100
|
-
private startTime: number = Date.now();
|
|
101
|
-
private isShuttingDown: boolean = false;
|
|
102
|
-
|
|
103
|
-
constructor(options: ClusterOptions = {}) {
|
|
104
|
-
super();
|
|
105
|
-
const numCPUs = cpus().length;
|
|
106
|
-
|
|
107
|
-
this.options = {
|
|
108
|
-
workers: options.workers === 'auto' ? numCPUs : (options.workers ?? numCPUs),
|
|
109
|
-
restartStrategy: options.restartStrategy ?? 'rolling',
|
|
110
|
-
restartDelay: options.restartDelay ?? 5000,
|
|
111
|
-
maxRestarts: options.maxRestarts ?? 10,
|
|
112
|
-
restartWindow: options.restartWindow ?? 60000,
|
|
113
|
-
onWorkerStart: options.onWorkerStart ?? (() => {}),
|
|
114
|
-
onWorkerExit: options.onWorkerExit ?? (() => {}),
|
|
115
|
-
onWorkerMessage: options.onWorkerMessage ?? (() => {}),
|
|
116
|
-
stickySessions: options.stickySessions ?? false,
|
|
117
|
-
verbose: options.verbose ?? false,
|
|
118
|
-
env: options.env ?? {}
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Check if current process is the primary/master
|
|
124
|
-
*/
|
|
125
|
-
isPrimary(): boolean {
|
|
126
|
-
return cluster.isPrimary || cluster.isMaster;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Check if current process is a worker
|
|
131
|
-
*/
|
|
132
|
-
isWorker(): boolean {
|
|
133
|
-
return cluster.isWorker;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Get current worker ID (only valid in worker process)
|
|
138
|
-
*/
|
|
139
|
-
getWorkerId(): number | undefined {
|
|
140
|
-
return cluster.worker?.id;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Start the cluster
|
|
145
|
-
*/
|
|
146
|
-
start(workerFn: () => void | Promise<void>): void {
|
|
147
|
-
if (this.isPrimary()) {
|
|
148
|
-
this.startPrimary();
|
|
149
|
-
} else {
|
|
150
|
-
this.startWorker(workerFn);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Start the primary process
|
|
156
|
-
*/
|
|
157
|
-
private startPrimary(): void {
|
|
158
|
-
const numWorkers = this.options.workers as number;
|
|
159
|
-
this.log(`Primary ${process.pid} is running`);
|
|
160
|
-
this.log(`Starting ${numWorkers} workers...`);
|
|
161
|
-
|
|
162
|
-
// Fork workers
|
|
163
|
-
for (let i = 0; i < numWorkers; i++) {
|
|
164
|
-
this.forkWorker();
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Handle worker events
|
|
168
|
-
cluster.on('online', (worker) => {
|
|
169
|
-
this.updateWorkerStatus(worker.id, 'online');
|
|
170
|
-
this.log(`Worker ${worker.id} (pid: ${worker.process.pid}) is online`);
|
|
171
|
-
this.options.onWorkerStart(worker);
|
|
172
|
-
this.emit('worker:online', worker);
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
cluster.on('listening', (worker, address) => {
|
|
176
|
-
this.updateWorkerStatus(worker.id, 'listening');
|
|
177
|
-
this.log(`Worker ${worker.id} is listening on ${address.port}`);
|
|
178
|
-
this.emit('worker:listening', worker, address);
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
cluster.on('disconnect', (worker) => {
|
|
182
|
-
this.updateWorkerStatus(worker.id, 'disconnecting');
|
|
183
|
-
this.log(`Worker ${worker.id} disconnected`);
|
|
184
|
-
this.emit('worker:disconnect', worker);
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
cluster.on('exit', (worker, code, signal) => {
|
|
188
|
-
this.handleWorkerExit(worker, code, signal);
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
cluster.on('message', (worker, message) => {
|
|
192
|
-
this.options.onWorkerMessage(worker, message);
|
|
193
|
-
this.emit('worker:message', worker, message);
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
// Handle graceful shutdown
|
|
197
|
-
process.on('SIGTERM', () => this.gracefulShutdown());
|
|
198
|
-
process.on('SIGINT', () => this.gracefulShutdown());
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Start a worker process
|
|
203
|
-
*/
|
|
204
|
-
private startWorker(workerFn: () => void | Promise<void>): void {
|
|
205
|
-
this.log(`Worker ${cluster.worker?.id} (pid: ${process.pid}) starting...`);
|
|
206
|
-
Promise.resolve(workerFn()).catch((err) => {
|
|
207
|
-
console.error(`Worker ${cluster.worker?.id} failed to start:`, err);
|
|
208
|
-
process.exit(1);
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Fork a new worker
|
|
214
|
-
*/
|
|
215
|
-
private forkWorker(): Worker {
|
|
216
|
-
const worker = cluster.fork(this.options.env);
|
|
217
|
-
|
|
218
|
-
this.workerInfo.set(worker.id, {
|
|
219
|
-
id: worker.id,
|
|
220
|
-
pid: worker.process.pid!,
|
|
221
|
-
startTime: Date.now(),
|
|
222
|
-
restartCount: 0,
|
|
223
|
-
status: 'starting'
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
return worker;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Handle worker exit
|
|
231
|
-
*/
|
|
232
|
-
private handleWorkerExit(worker: Worker, code: number, signal: string): void {
|
|
233
|
-
this.updateWorkerStatus(worker.id, 'dead');
|
|
234
|
-
this.log(`Worker ${worker.id} exited (code: ${code}, signal: ${signal})`);
|
|
235
|
-
this.options.onWorkerExit(worker, code, signal);
|
|
236
|
-
this.emit('worker:exit', worker, code, signal);
|
|
237
|
-
|
|
238
|
-
// Don't restart if shutting down
|
|
239
|
-
if (this.isShuttingDown) {
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Check restart limits
|
|
244
|
-
if (!this.canRestart(worker.id)) {
|
|
245
|
-
this.log(`Worker ${worker.id} exceeded restart limit, not restarting`);
|
|
246
|
-
this.emit('worker:restart-limit', worker.id);
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Restart based on strategy
|
|
251
|
-
if (this.options.restartStrategy !== 'none') {
|
|
252
|
-
this.scheduleRestart(worker.id);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Check if worker can be restarted
|
|
258
|
-
*/
|
|
259
|
-
private canRestart(workerId: number): boolean {
|
|
260
|
-
const now = Date.now();
|
|
261
|
-
const restartInfo = this.workerRestarts.get(workerId) || { count: 0, timestamps: [] };
|
|
262
|
-
|
|
263
|
-
// Clean old timestamps
|
|
264
|
-
restartInfo.timestamps = restartInfo.timestamps.filter(
|
|
265
|
-
(t) => now - t < this.options.restartWindow
|
|
266
|
-
);
|
|
267
|
-
|
|
268
|
-
return restartInfo.timestamps.length < this.options.maxRestarts;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* Schedule worker restart
|
|
273
|
-
*/
|
|
274
|
-
private scheduleRestart(workerId: number): void {
|
|
275
|
-
const delay = this.options.restartStrategy === 'rolling' ? this.options.restartDelay : 0;
|
|
276
|
-
|
|
277
|
-
this.log(`Scheduling restart for worker ${workerId} in ${delay}ms`);
|
|
278
|
-
|
|
279
|
-
setTimeout(() => {
|
|
280
|
-
if (this.isShuttingDown) return;
|
|
281
|
-
|
|
282
|
-
const restartInfo = this.workerRestarts.get(workerId) || { count: 0, timestamps: [] };
|
|
283
|
-
restartInfo.count++;
|
|
284
|
-
restartInfo.timestamps.push(Date.now());
|
|
285
|
-
this.workerRestarts.set(workerId, restartInfo);
|
|
286
|
-
|
|
287
|
-
const newWorker = this.forkWorker();
|
|
288
|
-
const info = this.workerInfo.get(newWorker.id);
|
|
289
|
-
if (info) {
|
|
290
|
-
info.restartCount = restartInfo.count;
|
|
291
|
-
info.lastRestartTime = Date.now();
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
this.log(`Restarted worker ${workerId} as worker ${newWorker.id}`);
|
|
295
|
-
this.emit('worker:restart', newWorker, workerId);
|
|
296
|
-
}, delay);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* Update worker status
|
|
301
|
-
*/
|
|
302
|
-
private updateWorkerStatus(workerId: number, status: WorkerInfo['status']): void {
|
|
303
|
-
const info = this.workerInfo.get(workerId);
|
|
304
|
-
if (info) {
|
|
305
|
-
info.status = status;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Send message to all workers
|
|
311
|
-
*/
|
|
312
|
-
broadcast(message: any): void {
|
|
313
|
-
if (!this.isPrimary()) return;
|
|
314
|
-
|
|
315
|
-
for (const id in cluster.workers) {
|
|
316
|
-
const worker = cluster.workers[id];
|
|
317
|
-
if (worker) {
|
|
318
|
-
worker.send(message);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* Send message to specific worker
|
|
325
|
-
*/
|
|
326
|
-
sendToWorker(workerId: number, message: any): boolean {
|
|
327
|
-
if (!this.isPrimary()) return false;
|
|
328
|
-
|
|
329
|
-
const worker = cluster.workers?.[workerId];
|
|
330
|
-
if (worker) {
|
|
331
|
-
worker.send(message);
|
|
332
|
-
return true;
|
|
333
|
-
}
|
|
334
|
-
return false;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Rolling restart all workers
|
|
339
|
-
*/
|
|
340
|
-
async rollingRestart(): Promise<void> {
|
|
341
|
-
if (!this.isPrimary()) return;
|
|
342
|
-
|
|
343
|
-
this.log('Starting rolling restart...');
|
|
344
|
-
const workerIds = Object.keys(cluster.workers || {}).map(Number);
|
|
345
|
-
|
|
346
|
-
for (const id of workerIds) {
|
|
347
|
-
const worker = cluster.workers?.[id];
|
|
348
|
-
if (worker) {
|
|
349
|
-
// Fork new worker first
|
|
350
|
-
const newWorker = this.forkWorker();
|
|
351
|
-
|
|
352
|
-
// Wait for new worker to be listening
|
|
353
|
-
await new Promise<void>((resolve) => {
|
|
354
|
-
const onListening = (w: Worker) => {
|
|
355
|
-
if (w.id === newWorker.id) {
|
|
356
|
-
cluster.off('listening', onListening);
|
|
357
|
-
resolve();
|
|
358
|
-
}
|
|
359
|
-
};
|
|
360
|
-
cluster.on('listening', onListening);
|
|
361
|
-
|
|
362
|
-
// Timeout after 30 seconds
|
|
363
|
-
setTimeout(resolve, 30000);
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
// Gracefully kill old worker
|
|
367
|
-
worker.disconnect();
|
|
368
|
-
|
|
369
|
-
// Wait for restart delay
|
|
370
|
-
await new Promise((resolve) => setTimeout(resolve, this.options.restartDelay));
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
this.log('Rolling restart complete');
|
|
375
|
-
this.emit('cluster:rolling-restart-complete');
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* Get cluster statistics
|
|
380
|
-
*/
|
|
381
|
-
getStats(): ClusterStats {
|
|
382
|
-
const workers: WorkerInfo[] = [];
|
|
383
|
-
let activeWorkers = 0;
|
|
384
|
-
let totalRestarts = 0;
|
|
385
|
-
|
|
386
|
-
for (const [id, info] of this.workerInfo) {
|
|
387
|
-
// Update memory usage if worker is alive
|
|
388
|
-
const worker = cluster.workers?.[id];
|
|
389
|
-
if (worker) {
|
|
390
|
-
activeWorkers++;
|
|
391
|
-
}
|
|
392
|
-
workers.push({ ...info });
|
|
393
|
-
totalRestarts += info.restartCount;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
return {
|
|
397
|
-
workers,
|
|
398
|
-
totalWorkers: this.workerInfo.size,
|
|
399
|
-
activeWorkers,
|
|
400
|
-
uptime: Date.now() - this.startTime,
|
|
401
|
-
totalRestarts,
|
|
402
|
-
isPrimary: this.isPrimary()
|
|
403
|
-
};
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
/**
|
|
407
|
-
* Graceful shutdown of all workers
|
|
408
|
-
*/
|
|
409
|
-
async gracefulShutdown(): Promise<void> {
|
|
410
|
-
if (this.isShuttingDown) return;
|
|
411
|
-
this.isShuttingDown = true;
|
|
412
|
-
|
|
413
|
-
this.log('Initiating graceful shutdown...');
|
|
414
|
-
this.emit('cluster:shutdown:start');
|
|
415
|
-
|
|
416
|
-
const workers = Object.values(cluster.workers || {}).filter(Boolean) as Worker[];
|
|
417
|
-
|
|
418
|
-
// Disconnect all workers
|
|
419
|
-
for (const worker of workers) {
|
|
420
|
-
worker.disconnect();
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// Wait for all workers to exit (with timeout)
|
|
424
|
-
await Promise.race([
|
|
425
|
-
Promise.all(
|
|
426
|
-
workers.map(
|
|
427
|
-
(worker) =>
|
|
428
|
-
new Promise<void>((resolve) => {
|
|
429
|
-
if (worker.isDead()) {
|
|
430
|
-
resolve();
|
|
431
|
-
} else {
|
|
432
|
-
worker.on('exit', () => resolve());
|
|
433
|
-
}
|
|
434
|
-
})
|
|
435
|
-
)
|
|
436
|
-
),
|
|
437
|
-
new Promise((resolve) => setTimeout(resolve, 30000))
|
|
438
|
-
]);
|
|
439
|
-
|
|
440
|
-
this.log('All workers shut down');
|
|
441
|
-
this.emit('cluster:shutdown:complete');
|
|
442
|
-
|
|
443
|
-
process.exit(0);
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
private log(message: string): void {
|
|
447
|
-
if (this.options.verbose) {
|
|
448
|
-
const prefix = this.isPrimary() ? '[Primary]' : `[Worker ${cluster.worker?.id}]`;
|
|
449
|
-
console.log(`${prefix} ${message}`);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
/**
|
|
455
|
-
* Create a cluster manager
|
|
456
|
-
*/
|
|
457
|
-
export function createCluster(options?: ClusterOptions): ClusterManager {
|
|
458
|
-
return new ClusterManager(options);
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
/**
|
|
462
|
-
* Simple cluster helper for common use case
|
|
463
|
-
*/
|
|
464
|
-
export function runClustered(
|
|
465
|
-
workerFn: () => void | Promise<void>,
|
|
466
|
-
options?: ClusterOptions
|
|
467
|
-
): ClusterManager {
|
|
468
|
-
const manager = createCluster(options);
|
|
469
|
-
manager.start(workerFn);
|
|
470
|
-
return manager;
|
|
471
|
-
}
|