neex 0.6.55 → 0.6.60
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.
|
@@ -90,17 +90,29 @@ function addStartCommands(program) {
|
|
|
90
90
|
const isDevelopment = options.watch || process.env.NODE_ENV === 'development';
|
|
91
91
|
const isProduction = !isDevelopment;
|
|
92
92
|
const cpuCount = os_1.default.cpus().length;
|
|
93
|
-
// Smart worker allocation
|
|
93
|
+
// Smart worker allocation - more conservative approach
|
|
94
94
|
let workers = options.workers;
|
|
95
95
|
if (!workers) {
|
|
96
96
|
if (isDevelopment) {
|
|
97
|
-
workers = 1;
|
|
97
|
+
workers = 1; // Always single worker in development
|
|
98
98
|
}
|
|
99
99
|
else {
|
|
100
|
-
// For production,
|
|
101
|
-
|
|
100
|
+
// For production, be more conservative
|
|
101
|
+
if (cpuCount <= 2) {
|
|
102
|
+
workers = 1; // Single worker for small systems
|
|
103
|
+
}
|
|
104
|
+
else if (cpuCount <= 4) {
|
|
105
|
+
workers = 2; // Two workers for medium systems
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
workers = Math.min(Math.floor(cpuCount / 2), 4); // Max 4 workers
|
|
109
|
+
}
|
|
102
110
|
}
|
|
103
111
|
}
|
|
112
|
+
// Force single worker for development or debugging
|
|
113
|
+
if (isDevelopment || options.inspect || options.inspectBrk) {
|
|
114
|
+
workers = 1;
|
|
115
|
+
}
|
|
104
116
|
const healthCheck = options.health !== false;
|
|
105
117
|
const defaultPort = parseInt(process.env.PORT || '8000');
|
|
106
118
|
const port = options.port || defaultPort;
|
|
@@ -118,22 +130,26 @@ function addStartCommands(program) {
|
|
|
118
130
|
logger_manager_js_1.loggerManager.printLine(`Environment: ${process.env.NODE_ENV}`);
|
|
119
131
|
logger_manager_js_1.loggerManager.printLine(`Port: ${port}`);
|
|
120
132
|
logger_manager_js_1.loggerManager.printLine(`Workers: ${workers}`);
|
|
133
|
+
logger_manager_js_1.loggerManager.printLine(`CPU Cores: ${cpuCount}`);
|
|
121
134
|
if (healthCheck) {
|
|
122
135
|
logger_manager_js_1.loggerManager.printLine(`Health Check: http://localhost:${options.healthPort}/health`);
|
|
123
136
|
}
|
|
124
137
|
}
|
|
138
|
+
// Optimize memory settings based on workers
|
|
139
|
+
const memoryPerWorker = workers > 1 ? Math.floor(512 / workers) : 512;
|
|
140
|
+
const maxMemoryPerWorker = workers > 1 ? Math.floor(1024 / workers) : 1024;
|
|
125
141
|
startManager = new start_manager_js_1.StartManager({
|
|
126
142
|
file: resolvedFile,
|
|
127
143
|
workingDir: options.dir,
|
|
128
144
|
envFile: options.env,
|
|
129
145
|
port,
|
|
130
146
|
workers,
|
|
131
|
-
memoryLimit: isProduction ?
|
|
147
|
+
memoryLimit: isProduction ? `${memoryPerWorker}M` : undefined,
|
|
132
148
|
logLevel: options.verbose ? 'debug' : 'info',
|
|
133
149
|
color: process.stdout.isTTY,
|
|
134
150
|
verbose: options.verbose,
|
|
135
151
|
watch: options.watch,
|
|
136
|
-
maxMemory: options.maxMemory || (isProduction ?
|
|
152
|
+
maxMemory: options.maxMemory || (isProduction ? `${maxMemoryPerWorker}M` : undefined),
|
|
137
153
|
maxCrashes: isProduction ? 3 : 10,
|
|
138
154
|
restartDelay: isProduction ? 1000 : 500,
|
|
139
155
|
healthCheck,
|
|
@@ -24,6 +24,7 @@ class StartManager {
|
|
|
24
24
|
this.totalRestarts = 0;
|
|
25
25
|
this.envLoaded = false;
|
|
26
26
|
this.masterProcess = null;
|
|
27
|
+
this.readyWorkers = 0;
|
|
27
28
|
this.options = options;
|
|
28
29
|
this.startTime = new Date();
|
|
29
30
|
this.debouncedRestart = (0, lodash_1.debounce)(this.restartAll.bind(this), options.restartDelay);
|
|
@@ -86,10 +87,10 @@ class StartManager {
|
|
|
86
87
|
args.push(`--max-old-space-size=${memoryMB}`);
|
|
87
88
|
}
|
|
88
89
|
}
|
|
89
|
-
if (this.options.inspect) {
|
|
90
|
+
if (this.options.inspect && this.options.workers === 1) {
|
|
90
91
|
args.push('--inspect');
|
|
91
92
|
}
|
|
92
|
-
if (this.options.inspectBrk) {
|
|
93
|
+
if (this.options.inspectBrk && this.options.workers === 1) {
|
|
93
94
|
args.push('--inspect-brk');
|
|
94
95
|
}
|
|
95
96
|
if (this.options.nodeArgs) {
|
|
@@ -98,6 +99,7 @@ class StartManager {
|
|
|
98
99
|
return args;
|
|
99
100
|
}
|
|
100
101
|
async startSingleProcess() {
|
|
102
|
+
var _a, _b;
|
|
101
103
|
const nodeArgs = this.getNodeArgs();
|
|
102
104
|
const port = this.options.port || 8000;
|
|
103
105
|
const env = {
|
|
@@ -105,13 +107,31 @@ class StartManager {
|
|
|
105
107
|
NODE_ENV: process.env.NODE_ENV || 'production',
|
|
106
108
|
PORT: port.toString(),
|
|
107
109
|
FORCE_COLOR: this.options.color ? '1' : '0',
|
|
108
|
-
NODE_OPTIONS: '--no-deprecation'
|
|
110
|
+
NODE_OPTIONS: '--no-deprecation',
|
|
111
|
+
CLUSTER_WORKER: 'false',
|
|
112
|
+
MASTER_PROCESS: 'true'
|
|
109
113
|
};
|
|
114
|
+
if (this.options.verbose) {
|
|
115
|
+
this.log(`Starting single process on port ${port}`);
|
|
116
|
+
}
|
|
110
117
|
this.masterProcess = (0, child_process_1.fork)(this.options.file, [], {
|
|
111
118
|
cwd: this.options.workingDir,
|
|
112
119
|
env,
|
|
113
120
|
execArgv: nodeArgs,
|
|
114
|
-
silent:
|
|
121
|
+
silent: true
|
|
122
|
+
});
|
|
123
|
+
// Handle process output with filtering
|
|
124
|
+
(_a = this.masterProcess.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
|
|
125
|
+
const message = data.toString().trim();
|
|
126
|
+
if (message && !this.shouldFilterMessage(message)) {
|
|
127
|
+
console.log(message);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
(_b = this.masterProcess.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (data) => {
|
|
131
|
+
const message = data.toString().trim();
|
|
132
|
+
if (message && !this.shouldFilterMessage(message)) {
|
|
133
|
+
console.error(chalk_1.default.red(message));
|
|
134
|
+
}
|
|
115
135
|
});
|
|
116
136
|
this.masterProcess.on('error', (error) => {
|
|
117
137
|
this.log(`Process error: ${error.message}`, 'error');
|
|
@@ -156,9 +176,21 @@ class StartManager {
|
|
|
156
176
|
});
|
|
157
177
|
});
|
|
158
178
|
if (this.options.verbose) {
|
|
159
|
-
this.log(`Process started (PID: ${this.masterProcess.pid}
|
|
179
|
+
this.log(`Process started (PID: ${this.masterProcess.pid})`);
|
|
160
180
|
}
|
|
161
181
|
}
|
|
182
|
+
shouldFilterMessage(message) {
|
|
183
|
+
const filters = [
|
|
184
|
+
'[dotenv@',
|
|
185
|
+
'injecting env',
|
|
186
|
+
'Express (',
|
|
187
|
+
'Local: http://localhost:',
|
|
188
|
+
'Health Check: http://localhost:',
|
|
189
|
+
'Environment: production',
|
|
190
|
+
'Environment: development'
|
|
191
|
+
];
|
|
192
|
+
return filters.some(filter => message.includes(filter));
|
|
193
|
+
}
|
|
162
194
|
async startWorker(workerId) {
|
|
163
195
|
var _a, _b;
|
|
164
196
|
const nodeArgs = this.getNodeArgs();
|
|
@@ -169,9 +201,14 @@ class StartManager {
|
|
|
169
201
|
WORKER_ID: workerId.toString(),
|
|
170
202
|
PORT: port.toString(),
|
|
171
203
|
CLUSTER_WORKER: 'true',
|
|
204
|
+
MASTER_PROCESS: 'false',
|
|
172
205
|
FORCE_COLOR: this.options.color ? '1' : '0',
|
|
173
|
-
NODE_OPTIONS: '--no-deprecation'
|
|
206
|
+
NODE_OPTIONS: '--no-deprecation',
|
|
207
|
+
SUPPRESS_STARTUP_LOGS: 'true' // Signal to suppress duplicate logs
|
|
174
208
|
};
|
|
209
|
+
if (this.options.verbose) {
|
|
210
|
+
this.log(`Starting worker ${workerId}`);
|
|
211
|
+
}
|
|
175
212
|
const workerProcess = (0, child_process_1.fork)(this.options.file, [], {
|
|
176
213
|
cwd: this.options.workingDir,
|
|
177
214
|
env,
|
|
@@ -184,13 +221,14 @@ class StartManager {
|
|
|
184
221
|
restarts: 0,
|
|
185
222
|
startTime: new Date(),
|
|
186
223
|
id: workerId,
|
|
187
|
-
port: port
|
|
224
|
+
port: port,
|
|
225
|
+
ready: false
|
|
188
226
|
};
|
|
189
227
|
this.workers.set(workerId, workerInfo);
|
|
190
|
-
// Handle worker output
|
|
228
|
+
// Handle worker output with filtering
|
|
191
229
|
(_a = workerProcess.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
|
|
192
230
|
const message = data.toString().trim();
|
|
193
|
-
if (message && !
|
|
231
|
+
if (message && !this.shouldFilterMessage(message)) {
|
|
194
232
|
const prefix = this.options.verbose ?
|
|
195
233
|
chalk_1.default.dim(`[Worker ${workerId}] `) : '';
|
|
196
234
|
console.log(prefix + message);
|
|
@@ -198,7 +236,7 @@ class StartManager {
|
|
|
198
236
|
});
|
|
199
237
|
(_b = workerProcess.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (data) => {
|
|
200
238
|
const message = data.toString().trim();
|
|
201
|
-
if (message && !
|
|
239
|
+
if (message && !this.shouldFilterMessage(message)) {
|
|
202
240
|
const prefix = this.options.verbose ?
|
|
203
241
|
chalk_1.default.dim(`[Worker ${workerId}] `) : '';
|
|
204
242
|
console.error(prefix + chalk_1.default.red(message));
|
|
@@ -217,11 +255,15 @@ class StartManager {
|
|
|
217
255
|
// Wait for worker to be ready
|
|
218
256
|
await new Promise((resolve, reject) => {
|
|
219
257
|
const timeout = setTimeout(() => {
|
|
258
|
+
workerInfo.ready = true;
|
|
259
|
+
this.readyWorkers++;
|
|
220
260
|
resolve(); // Don't reject, assume it's ready
|
|
221
261
|
}, 5000);
|
|
222
262
|
workerProcess.on('message', (message) => {
|
|
223
263
|
if (message && message.type === 'ready') {
|
|
224
264
|
clearTimeout(timeout);
|
|
265
|
+
workerInfo.ready = true;
|
|
266
|
+
this.readyWorkers++;
|
|
225
267
|
resolve();
|
|
226
268
|
}
|
|
227
269
|
});
|
|
@@ -285,11 +327,11 @@ class StartManager {
|
|
|
285
327
|
}
|
|
286
328
|
async startCluster() {
|
|
287
329
|
if (this.options.workers === 1) {
|
|
288
|
-
// Single process mode
|
|
289
330
|
await this.startSingleProcess();
|
|
290
331
|
return;
|
|
291
332
|
}
|
|
292
333
|
// Multi-worker mode
|
|
334
|
+
this.readyWorkers = 0;
|
|
293
335
|
const startPromises = [];
|
|
294
336
|
for (let i = 0; i < this.options.workers; i++) {
|
|
295
337
|
startPromises.push(this.startWorker(i + 1));
|
|
@@ -315,16 +357,23 @@ class StartManager {
|
|
|
315
357
|
return;
|
|
316
358
|
}
|
|
317
359
|
if (req.url === '/health') {
|
|
360
|
+
const activeWorkers = this.options.workers === 1 ?
|
|
361
|
+
(this.masterProcess ? 1 : 0) :
|
|
362
|
+
this.workers.size;
|
|
318
363
|
const stats = {
|
|
319
364
|
status: 'ok',
|
|
320
365
|
uptime: Date.now() - this.startTime.getTime(),
|
|
321
|
-
workers:
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
pid:
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
366
|
+
workers: activeWorkers,
|
|
367
|
+
expectedWorkers: this.options.workers,
|
|
368
|
+
activeWorkers: this.options.workers === 1 ?
|
|
369
|
+
(this.masterProcess ? [{ id: 1, pid: this.masterProcess.pid, uptime: Date.now() - this.startTime.getTime() }] : []) :
|
|
370
|
+
Array.from(this.workers.values()).map(w => ({
|
|
371
|
+
id: w.id,
|
|
372
|
+
pid: w.pid,
|
|
373
|
+
restarts: w.restarts,
|
|
374
|
+
uptime: Date.now() - w.startTime.getTime(),
|
|
375
|
+
ready: w.ready
|
|
376
|
+
})),
|
|
328
377
|
totalRestarts: this.totalRestarts,
|
|
329
378
|
memory: process.memoryUsage(),
|
|
330
379
|
cpu: os_1.default.loadavg(),
|
|
@@ -379,7 +428,6 @@ class StartManager {
|
|
|
379
428
|
return;
|
|
380
429
|
this.log('Restarting due to file changes...');
|
|
381
430
|
if (this.options.workers === 1 && this.masterProcess) {
|
|
382
|
-
// Single process restart
|
|
383
431
|
try {
|
|
384
432
|
this.masterProcess.kill('SIGTERM');
|
|
385
433
|
await this.waitForProcessExit(this.masterProcess, 5000);
|
|
@@ -392,7 +440,6 @@ class StartManager {
|
|
|
392
440
|
}, this.options.restartDelay);
|
|
393
441
|
}
|
|
394
442
|
else {
|
|
395
|
-
// Multi-worker restart
|
|
396
443
|
const restartPromises = [];
|
|
397
444
|
for (const [workerId, workerInfo] of this.workers.entries()) {
|
|
398
445
|
restartPromises.push((async () => {
|
|
@@ -422,8 +469,9 @@ class StartManager {
|
|
|
422
469
|
await this.startCluster();
|
|
423
470
|
// Success message
|
|
424
471
|
const port = this.options.port || 8000;
|
|
472
|
+
const actualWorkers = this.options.workers === 1 ? 1 : this.workers.size;
|
|
425
473
|
const workerInfo = this.options.workers === 1 ?
|
|
426
|
-
'' : ` (${
|
|
474
|
+
'' : ` (${actualWorkers} workers)`;
|
|
427
475
|
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green(figures_1.default.tick)} Server ready on port ${port}${workerInfo}`, 'info');
|
|
428
476
|
}
|
|
429
477
|
catch (error) {
|