@cocreate/server-telemetry 1.15.0 → 1.16.0
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/CHANGELOG.md +7 -0
- package/package.json +1 -1
- package/src/index.js +47 -44
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# [1.16.0](https://github.com/CoCreate-app/CoCreate-server-telemetry/compare/v1.15.0...v1.16.0) (2026-07-18)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* streamline telemetry code by removing redundant comments and enhancing resource limit checks ([275c62c](https://github.com/CoCreate-app/CoCreate-server-telemetry/commit/275c62c52a484d743a2b84c108552839c786f243))
|
|
7
|
+
|
|
1
8
|
# [1.15.0](https://github.com/CoCreate-app/CoCreate-server-telemetry/compare/v1.14.3...v1.15.0) (2026-07-17)
|
|
2
9
|
|
|
3
10
|
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -26,10 +26,6 @@ import os from 'node:os';
|
|
|
26
26
|
import Config from '@cocreate/config';
|
|
27
27
|
import { getValueFromObject } from '@cocreate/utils';
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
// ==============================================================================
|
|
31
|
-
// Module-Level Sandbox State (ESM Singleton Container)
|
|
32
|
-
// ==============================================================================
|
|
33
29
|
let server = null; // Reference to the main CoCreateServer instance
|
|
34
30
|
let activeIntervalId = null; // Storage for the core execution loop interval
|
|
35
31
|
let lastCpuInfo = null; // Cache for the previous CPU telemetry sample
|
|
@@ -58,7 +54,6 @@ const defaults = {
|
|
|
58
54
|
broadcast: true
|
|
59
55
|
};
|
|
60
56
|
|
|
61
|
-
|
|
62
57
|
/**
|
|
63
58
|
* Reads memory allocations using a local-dev fallback if Linux /proc is unavailable.
|
|
64
59
|
* Supports cross-platform safety (macOS and Windows environments).
|
|
@@ -108,7 +103,6 @@ export function readMemoryInfo() {
|
|
|
108
103
|
}
|
|
109
104
|
}
|
|
110
105
|
|
|
111
|
-
|
|
112
106
|
/**
|
|
113
107
|
* Reads CPU performance ticks across cores natively or using fallback trackers.
|
|
114
108
|
*
|
|
@@ -144,7 +138,6 @@ export function readCpuInfo() {
|
|
|
144
138
|
}
|
|
145
139
|
}
|
|
146
140
|
|
|
147
|
-
|
|
148
141
|
/**
|
|
149
142
|
* Computes CPU core utilization percentages by comparing differential cycle ticks.
|
|
150
143
|
*
|
|
@@ -175,7 +168,6 @@ export function calculateCpuUsage(cpuInfo1, cpuInfo2) {
|
|
|
175
168
|
});
|
|
176
169
|
}
|
|
177
170
|
|
|
178
|
-
|
|
179
171
|
/**
|
|
180
172
|
* Calculates a dynamic safety memory threshold percentage based on the physical core-to-memory ratio.
|
|
181
173
|
* This is symmetrically checked locally in server-telemetry.js to align the 1-second emergency loops.
|
|
@@ -198,7 +190,6 @@ function calculateDynamicSafetyThreshold() {
|
|
|
198
190
|
return Math.max(50, Math.min(90, maxSafePercentage));
|
|
199
191
|
}
|
|
200
192
|
|
|
201
|
-
|
|
202
193
|
/**
|
|
203
194
|
* Dynamic Telemetry Ingestion Protocol.
|
|
204
195
|
* Allows other platform modules (like server-mesh.js) to push specific performance
|
|
@@ -222,7 +213,6 @@ export function send(component, data) {
|
|
|
222
213
|
}
|
|
223
214
|
}
|
|
224
215
|
|
|
225
|
-
|
|
226
216
|
/**
|
|
227
217
|
* Core execution tick. Collects system specs, appends external custom registries,
|
|
228
218
|
* and commits rolls to database buffer limits. Excludes event-emitter or callback leaks.
|
|
@@ -272,7 +262,6 @@ export function read() {
|
|
|
272
262
|
rss: Math.round(processMemory.rss / 1024),
|
|
273
263
|
external: Math.round(processMemory.external / 1024)
|
|
274
264
|
},
|
|
275
|
-
// Symmetrically merge custom component blocks directly into the heartbeat payload
|
|
276
265
|
...customTelemetryRegistry,
|
|
277
266
|
timestamp: new Date().toISOString()
|
|
278
267
|
};
|
|
@@ -280,13 +269,10 @@ export function read() {
|
|
|
280
269
|
dbBuffer.push(JSON.parse(JSON.stringify(currentTelemetry)));
|
|
281
270
|
|
|
282
271
|
// --- INSTANTANEOUS 1-SECOND OOM PROTECTION CHECK ---
|
|
283
|
-
// If memory spikes above the threshold on any individual 1-second sample,
|
|
284
|
-
// fire an immediate emergency signal to the Master process.
|
|
285
272
|
let targetMemoryThreshold = telemetryConfig
|
|
286
273
|
? parseInt(getValueFromObject(telemetryConfig, 'server-telemetry.scaleUpMemoryThreshold'), 10)
|
|
287
274
|
: 85;
|
|
288
275
|
|
|
289
|
-
// Apply Dynamic Safety limits to 1s emergency checks if enabled (on by default)
|
|
290
276
|
const isSafetyLimitsEnabled = getValueFromObject(telemetryConfig, 'server-telemetry.safetyLimits') !== false && getValueFromObject(telemetryConfig, 'server-telemetry.safetyLimits') !== 'false';
|
|
291
277
|
|
|
292
278
|
if (isSafetyLimitsEnabled) {
|
|
@@ -297,7 +283,7 @@ export function read() {
|
|
|
297
283
|
}
|
|
298
284
|
|
|
299
285
|
if (currentTelemetry.memory.percentage >= targetMemoryThreshold) {
|
|
300
|
-
const emergencyCooldown = 300000;
|
|
286
|
+
const emergencyCooldown = 300000;
|
|
301
287
|
if (Date.now() - lastEmergencyAlertTimestamp >= emergencyCooldown) {
|
|
302
288
|
lastEmergencyAlertTimestamp = Date.now();
|
|
303
289
|
console.warn(`[@cocreate/server-telemetry] [EMERGENCY] Memory threshold breached on 1s tick (${currentTelemetry.memory.percentage}% >= ${targetMemoryThreshold}%). Dispatching direct system-up scaling alarm!`);
|
|
@@ -309,9 +295,51 @@ export function read() {
|
|
|
309
295
|
}
|
|
310
296
|
}
|
|
311
297
|
|
|
312
|
-
//
|
|
313
|
-
//
|
|
314
|
-
|
|
298
|
+
// --- AUTOMATED INGRESS CORDON TRACKING (CLIENT-SIDE LOAD BALANCING) ---
|
|
299
|
+
// User-configurable limit nested inside safetyLimits, safetyOptions, or defined flat
|
|
300
|
+
const cordonMemoryThreshold = telemetryConfig
|
|
301
|
+
? parseInt(
|
|
302
|
+
getValueFromObject(telemetryConfig, 'server-telemetry.safetyLimits.cordonMemoryThreshold') ||
|
|
303
|
+
getValueFromObject(telemetryConfig, 'server-telemetry.safetyOptions.cordonMemoryThreshold') ||
|
|
304
|
+
getValueFromObject(telemetryConfig, 'server-telemetry.cordonMemoryThreshold'), 10
|
|
305
|
+
)
|
|
306
|
+
: 90;
|
|
307
|
+
|
|
308
|
+
const cordonCpuThreshold = telemetryConfig
|
|
309
|
+
? parseInt(
|
|
310
|
+
getValueFromObject(telemetryConfig, 'server-telemetry.safetyLimits.cordonCpuThreshold') ||
|
|
311
|
+
getValueFromObject(telemetryConfig, 'server-telemetry.safetyOptions.cordonCpuThreshold') ||
|
|
312
|
+
getValueFromObject(telemetryConfig, 'server-telemetry.cordonCpuThreshold'), 10
|
|
313
|
+
)
|
|
314
|
+
: 90;
|
|
315
|
+
|
|
316
|
+
const actualMemoryThreshold = isNaN(cordonMemoryThreshold) ? 90 : cordonMemoryThreshold;
|
|
317
|
+
const actualCpuThreshold = isNaN(cordonCpuThreshold) ? 90 : cordonCpuThreshold;
|
|
318
|
+
|
|
319
|
+
const isResourceLimitBreached = currentTelemetry.memory.percentage >= actualMemoryThreshold ||
|
|
320
|
+
currentTelemetry.cpu.totalUsage >= actualCpuThreshold;
|
|
321
|
+
|
|
322
|
+
const recoveryBuffer = 5;
|
|
323
|
+
const isResourceSafeToRecover = currentTelemetry.memory.percentage < (actualMemoryThreshold - recoveryBuffer) &&
|
|
324
|
+
currentTelemetry.cpu.totalUsage < (actualCpuThreshold - recoveryBuffer);
|
|
325
|
+
|
|
326
|
+
// Decoupled state execution: Telemetry acts as a sensor sending updates to the Server core.
|
|
327
|
+
// It relies on server.isCordon to verify if a state modification payload needs to be broadcast.
|
|
328
|
+
if (isResourceLimitBreached && !server.isCordon) {
|
|
329
|
+
server.send({
|
|
330
|
+
method: 'cordon',
|
|
331
|
+
broadcast: true,
|
|
332
|
+
active: true
|
|
333
|
+
});
|
|
334
|
+
} else if (!isResourceLimitBreached && server.isCordon && isResourceSafeToRecover) {
|
|
335
|
+
server.send({
|
|
336
|
+
method: 'cordon',
|
|
337
|
+
broadcast: true,
|
|
338
|
+
active: false
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Dynamic Time-Staggering Check
|
|
315
343
|
const customWriteInterval = telemetryConfig
|
|
316
344
|
? parseInt(getValueFromObject(telemetryConfig, 'server-telemetry.writeInterval'), 10)
|
|
317
345
|
: defaults.writeInterval;
|
|
@@ -327,12 +355,9 @@ export function read() {
|
|
|
327
355
|
return currentTelemetry;
|
|
328
356
|
}
|
|
329
357
|
|
|
330
|
-
|
|
331
358
|
/**
|
|
332
359
|
* Processes buffered raw tick records to calculate minimum, maximum, and average values.
|
|
333
360
|
* Writes aggregated results as a single document utilizing standard server.crud structures.
|
|
334
|
-
* Also emits the statistical telemetry directly to the IPC loop using fail-fast server.send().
|
|
335
|
-
* Cleans up outdated database telemetry if retentionDays is configured.
|
|
336
361
|
*/
|
|
337
362
|
export async function flushTelemetryToDatabase() {
|
|
338
363
|
if (dbBuffer.length === 0) {
|
|
@@ -348,7 +373,6 @@ export async function flushTelemetryToDatabase() {
|
|
|
348
373
|
const load1m = dbBuffer.map(sample => sample.system.loadavg[0]);
|
|
349
374
|
const heapUsed = dbBuffer.map(sample => sample.process.heapUsed);
|
|
350
375
|
|
|
351
|
-
// Compute statistical aggregates
|
|
352
376
|
const cpuMin = Math.min(...cpuUsages);
|
|
353
377
|
const cpuMax = Math.max(...cpuUsages);
|
|
354
378
|
const cpuAvg = cpuUsages.reduce((sum, val) => sum + val, 0) / totalSamples;
|
|
@@ -362,7 +386,6 @@ export async function flushTelemetryToDatabase() {
|
|
|
362
386
|
|
|
363
387
|
const lastSample = dbBuffer[totalSamples - 1];
|
|
364
388
|
|
|
365
|
-
// Gather any custom component keys injected during the current buffer window
|
|
366
389
|
const componentAggregates = {};
|
|
367
390
|
for (const key of Object.keys(customTelemetryRegistry)) {
|
|
368
391
|
componentAggregates[key] = lastSample[key] || null;
|
|
@@ -398,24 +421,19 @@ export async function flushTelemetryToDatabase() {
|
|
|
398
421
|
worker_uptime: lastSample.process.uptime,
|
|
399
422
|
max_heap_kb: maxHeap
|
|
400
423
|
},
|
|
401
|
-
// Save dynamic custom telemetry blocks
|
|
402
424
|
custom: componentAggregates
|
|
403
425
|
};
|
|
404
426
|
|
|
405
|
-
// Clear local buffer immediately to avoid execution loop overlap
|
|
406
427
|
dbBuffer = [];
|
|
407
428
|
lastDbFlushTimestamp = Date.now();
|
|
408
429
|
|
|
409
|
-
// 1. DISPATCH LIVE EVENT VIA SERVER SEND
|
|
410
|
-
// Directly fire the 'telemetry' method upstream.
|
|
411
|
-
// It should route cleanly or throw immediately.
|
|
430
|
+
// 1. DISPATCH LIVE EVENT VIA SERVER SEND
|
|
412
431
|
server.send({
|
|
413
432
|
method: 'telemetry',
|
|
414
433
|
data: aggregatedPayload
|
|
415
434
|
});
|
|
416
435
|
|
|
417
436
|
// 2. WRITE HISTORICAL RECORD TO DATABASE
|
|
418
|
-
// Target standard CoCreate server storage configuration using server.crud directly
|
|
419
437
|
const dbResponse = await server.crud.send({
|
|
420
438
|
method: 'object.create',
|
|
421
439
|
array: 'system_telemetry',
|
|
@@ -434,7 +452,6 @@ export async function flushTelemetryToDatabase() {
|
|
|
434
452
|
const isBroadcastEnabled = broadcastConfig !== false && broadcastConfig !== 'false';
|
|
435
453
|
|
|
436
454
|
if (isBroadcastEnabled) {
|
|
437
|
-
// Inject broadcast parameters onto the validated response object
|
|
438
455
|
dbResponse.broadcast = broadcastConfig;
|
|
439
456
|
server.wsManager.send(dbResponse);
|
|
440
457
|
}
|
|
@@ -447,7 +464,6 @@ export async function flushTelemetryToDatabase() {
|
|
|
447
464
|
|
|
448
465
|
const retentionDays = isNaN(configuredRetention) ? defaults.retentionDays : configuredRetention;
|
|
449
466
|
|
|
450
|
-
// Only Worker 1 handles retention pruning to prevent lock disputes
|
|
451
467
|
if (retentionDays > 0 && workerId === 1) {
|
|
452
468
|
const cutoffDate = new Date();
|
|
453
469
|
cutoffDate.setDate(cutoffDate.getDate() - retentionDays);
|
|
@@ -468,7 +484,6 @@ export async function flushTelemetryToDatabase() {
|
|
|
468
484
|
}
|
|
469
485
|
}
|
|
470
486
|
|
|
471
|
-
|
|
472
487
|
/**
|
|
473
488
|
* Returns the cached memory and system snapshot compiled during the last execution sweep.
|
|
474
489
|
*
|
|
@@ -480,15 +495,10 @@ export function get() {
|
|
|
480
495
|
|
|
481
496
|
/**
|
|
482
497
|
* Integrates server-telemetry capabilities, loads configuration, and schedules staggered execution threads.
|
|
483
|
-
* Returns the functional API contract directly instead of mutating the parent server context.
|
|
484
|
-
*
|
|
485
|
-
* @param {Object} serverCtx - The core instantiated server environment
|
|
486
|
-
* @returns {Object} Functional API contract for the server-telemetry subsystem
|
|
487
498
|
*/
|
|
488
499
|
export async function init(serverCtx) {
|
|
489
500
|
server = serverCtx;
|
|
490
501
|
|
|
491
|
-
// Define functional API contract
|
|
492
502
|
const telemetryAPI = {
|
|
493
503
|
init,
|
|
494
504
|
stop,
|
|
@@ -501,7 +511,6 @@ export async function init(serverCtx) {
|
|
|
501
511
|
flushTelemetryToDatabase
|
|
502
512
|
};
|
|
503
513
|
|
|
504
|
-
// Load nested telemetry parameters matching standard configuration queries
|
|
505
514
|
telemetryConfig = await Config({
|
|
506
515
|
'server-telemetry': ''
|
|
507
516
|
});
|
|
@@ -518,16 +527,11 @@ export async function init(serverCtx) {
|
|
|
518
527
|
|
|
519
528
|
const workerId = server.workerId;
|
|
520
529
|
const totalWorkers = server.totalWorkers;
|
|
521
|
-
|
|
522
|
-
// Staggered launch delays to prevent resource spikes during multi-worker server boots
|
|
523
|
-
const staggeredDelay = (workerId * collectionInterval) + (totalWorkers * collectionInterval);
|
|
524
530
|
|
|
525
531
|
stop();
|
|
526
532
|
|
|
527
533
|
lastCpuInfo = readCpuInfo();
|
|
528
534
|
|
|
529
|
-
// Mathematically offset the first write target so that worker logging phases
|
|
530
|
-
// are perfectly staggered across the write window, avoiding write overlaps!
|
|
531
535
|
const phaseOffset = ((workerId - 1) / totalWorkers) * customWriteInterval;
|
|
532
536
|
lastDbFlushTimestamp = Date.now() - phaseOffset;
|
|
533
537
|
|
|
@@ -546,7 +550,6 @@ export function stop() {
|
|
|
546
550
|
}
|
|
547
551
|
}
|
|
548
552
|
|
|
549
|
-
// Default export matches standard functional API entry contract
|
|
550
553
|
export default {
|
|
551
554
|
init,
|
|
552
555
|
stop,
|