@platformatic/runtime 3.52.2 → 3.52.3
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/lib/runtime.js +62 -5
- package/lib/worker/symbols.js +1 -0
- package/package.json +15 -15
- package/schema.json +1 -1
package/lib/runtime.js
CHANGED
|
@@ -61,6 +61,7 @@ import {
|
|
|
61
61
|
kHealthCheckTimer,
|
|
62
62
|
kId,
|
|
63
63
|
kInterceptorReadyPromise,
|
|
64
|
+
kIsSubprocessHost,
|
|
64
65
|
kITC,
|
|
65
66
|
kLastHealthCheckELU,
|
|
66
67
|
kStderrMarker,
|
|
@@ -73,6 +74,9 @@ import {
|
|
|
73
74
|
|
|
74
75
|
const kWorkerFile = join(import.meta.dirname, 'worker/main.js')
|
|
75
76
|
const kInspectorOptions = Symbol('plt.runtime.worker.inspectorOptions')
|
|
77
|
+
const kHeapCheckCounter = Symbol('plt.runtime.worker.heapCheckCounter')
|
|
78
|
+
const kLastHeapStats = Symbol('plt.runtime.worker.lastHeapStats')
|
|
79
|
+
const kHealthITCTimeoutMs = 5000
|
|
76
80
|
|
|
77
81
|
const MAX_LISTENERS_COUNT = 100
|
|
78
82
|
|
|
@@ -1515,12 +1519,57 @@ export class Runtime extends EventEmitter {
|
|
|
1515
1519
|
return status
|
|
1516
1520
|
}
|
|
1517
1521
|
|
|
1518
|
-
|
|
1519
|
-
//
|
|
1520
|
-
//
|
|
1521
|
-
//
|
|
1522
|
-
|
|
1522
|
+
getWorkerHealth (worker, options = {}) {
|
|
1523
|
+
// For subprocess workers we must round-trip through ITC to reach the child;
|
|
1524
|
+
// for pure worker-thread workers we can read ELU/heap directly from the
|
|
1525
|
+
// worker handle, which is served by Node's C++ layer and does not depend
|
|
1526
|
+
// on the worker's event loop being responsive. Going through ITC for
|
|
1527
|
+
// thread workers means a CPU-bound or stuck worker can freeze the whole
|
|
1528
|
+
// health-collection loop, which in turn blocks the management API.
|
|
1529
|
+
if (worker[kIsSubprocessHost]) {
|
|
1530
|
+
return this.#getSubprocessWorkerHealth(worker, options)
|
|
1531
|
+
}
|
|
1523
1532
|
|
|
1533
|
+
const currentELU = worker.performance.eventLoopUtilization()
|
|
1534
|
+
const previousELU = options.previousELU
|
|
1535
|
+
|
|
1536
|
+
let elu = currentELU
|
|
1537
|
+
if (previousELU) {
|
|
1538
|
+
elu = worker.performance.eventLoopUtilization(elu, previousELU)
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
if (!features.node.worker.getHeapStatistics) {
|
|
1542
|
+
return { elu: elu.utilization, currentELU }
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
// Only refresh heap statistics every 60 health checks (once per minute).
|
|
1546
|
+
// This keeps the common path fully synchronous — no promise allocation.
|
|
1547
|
+
const counter = (worker[kHeapCheckCounter] ?? 0) + 1
|
|
1548
|
+
worker[kHeapCheckCounter] = counter >= 60 ? 0 : counter
|
|
1549
|
+
|
|
1550
|
+
if (counter >= 60 || !worker[kLastHeapStats]) {
|
|
1551
|
+
return worker.getHeapStatistics().then(({ used_heap_size: heapUsed, total_heap_size: heapTotal }) => {
|
|
1552
|
+
worker[kLastHeapStats] = { heapUsed, heapTotal }
|
|
1553
|
+
return { elu: elu.utilization, heapUsed, heapTotal, currentELU }
|
|
1554
|
+
})
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
const { heapUsed, heapTotal } = worker[kLastHeapStats]
|
|
1558
|
+
return { elu: elu.utilization, heapUsed, heapTotal, currentELU }
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
async #getSubprocessWorkerHealth (worker, options) {
|
|
1562
|
+
// Bound the ITC call so a hung child cannot freeze the health loop.
|
|
1563
|
+
// On timeout we fall back to last-known ELU with a null heap reading so
|
|
1564
|
+
// that the loop keeps rescheduling and signals remain observable.
|
|
1565
|
+
const result = await executeWithTimeout(sendViaITC(worker, 'getHealth'), kHealthITCTimeoutMs, kTimeout)
|
|
1566
|
+
|
|
1567
|
+
if (result === kTimeout) {
|
|
1568
|
+
const previousELU = options.previousELU ?? worker.performance.eventLoopUtilization()
|
|
1569
|
+
return { elu: 1, heapUsed: null, heapTotal: null, currentELU: previousELU }
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
const { currentELU, heapUsed, heapTotal } = result
|
|
1524
1573
|
const previousELU = options.previousELU
|
|
1525
1574
|
let elu = currentELU
|
|
1526
1575
|
if (previousELU) {
|
|
@@ -1853,6 +1902,14 @@ export class Runtime extends EventEmitter {
|
|
|
1853
1902
|
this.logger.trace({ event, payload, id: workerId, application: applicationId, worker: index }, 'Runtime event')
|
|
1854
1903
|
})
|
|
1855
1904
|
|
|
1905
|
+
// The worker notifies us when its capability has spawned a child process
|
|
1906
|
+
// (e.g. Next.js in dev mode). From that point on health metrics must come
|
|
1907
|
+
// from the child via ITC; for thread-only workers we keep reading the
|
|
1908
|
+
// handle directly in getWorkerHealth().
|
|
1909
|
+
worker[kITC].on('subprocess:started', () => {
|
|
1910
|
+
worker[kIsSubprocessHost] = true
|
|
1911
|
+
})
|
|
1912
|
+
|
|
1856
1913
|
worker[kITC].on('request:restart', async () => {
|
|
1857
1914
|
// Do not restart applications that are not fully started yet or when the runtime is still starting.
|
|
1858
1915
|
// The gateway sends request:restart when it receives application:added events,
|
package/lib/worker/symbols.js
CHANGED
|
@@ -12,6 +12,7 @@ export const kInterceptors = Symbol.for('plt.runtime.worker.interceptors')
|
|
|
12
12
|
export const kLastHealthCheckELU = Symbol.for('plt.runtime.worker.lastHealthCheckELU')
|
|
13
13
|
export const kLastWorkerScalerELU = Symbol.for('plt.runtime.worker.lastWorkerScalerELU')
|
|
14
14
|
export const kInterceptorReadyPromise = Symbol.for('plt.runtime.worker.interceptorReadyPromise')
|
|
15
|
+
export const kIsSubprocessHost = Symbol.for('plt.runtime.worker.isSubprocessHost')
|
|
15
16
|
|
|
16
17
|
// This string marker should be safe to use since it belongs to Unicode private area
|
|
17
18
|
export const kStderrMarker = '\ue002'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/runtime",
|
|
3
|
-
"version": "3.52.
|
|
3
|
+
"version": "3.52.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -36,14 +36,14 @@
|
|
|
36
36
|
"typescript": "^5.5.4",
|
|
37
37
|
"undici-oidc-interceptor": "^0.5.0",
|
|
38
38
|
"why-is-node-running": "^2.2.2",
|
|
39
|
-
"@platformatic/composer": "3.52.
|
|
40
|
-
"@platformatic/
|
|
41
|
-
"@platformatic/
|
|
42
|
-
"@platformatic/node": "3.52.
|
|
43
|
-
"@platformatic/service": "3.52.
|
|
44
|
-
"@platformatic/sql-
|
|
45
|
-
"@platformatic/sql-
|
|
46
|
-
"@platformatic/wattpm-pprof-capture": "3.52.
|
|
39
|
+
"@platformatic/composer": "3.52.3",
|
|
40
|
+
"@platformatic/db": "3.52.3",
|
|
41
|
+
"@platformatic/gateway": "3.52.3",
|
|
42
|
+
"@platformatic/node": "3.52.3",
|
|
43
|
+
"@platformatic/service": "3.52.3",
|
|
44
|
+
"@platformatic/sql-mapper": "3.52.3",
|
|
45
|
+
"@platformatic/sql-graphql": "3.52.3",
|
|
46
|
+
"@platformatic/wattpm-pprof-capture": "3.52.3"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@fastify/accepts": "^5.0.0",
|
|
@@ -73,12 +73,12 @@
|
|
|
73
73
|
"undici": "^7.0.0",
|
|
74
74
|
"undici-thread-interceptor": "^1.3.1",
|
|
75
75
|
"ws": "^8.16.0",
|
|
76
|
-
"@platformatic/basic": "3.52.
|
|
77
|
-
"@platformatic/
|
|
78
|
-
"@platformatic/
|
|
79
|
-
"@platformatic/metrics": "3.52.
|
|
80
|
-
"@platformatic/
|
|
81
|
-
"@platformatic/
|
|
76
|
+
"@platformatic/basic": "3.52.3",
|
|
77
|
+
"@platformatic/foundation": "3.52.3",
|
|
78
|
+
"@platformatic/generators": "3.52.3",
|
|
79
|
+
"@platformatic/metrics": "3.52.3",
|
|
80
|
+
"@platformatic/telemetry": "3.52.3",
|
|
81
|
+
"@platformatic/itc": "3.52.3"
|
|
82
82
|
},
|
|
83
83
|
"engines": {
|
|
84
84
|
"node": ">=22.19.0"
|
package/schema.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://schemas.platformatic.dev/@platformatic/runtime/3.52.
|
|
2
|
+
"$id": "https://schemas.platformatic.dev/@platformatic/runtime/3.52.3.json",
|
|
3
3
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
4
|
"title": "Platformatic Runtime Config",
|
|
5
5
|
"type": "object",
|