@platformatic/runtime 3.52.3 → 3.53.0-alpha.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/lib/runtime.js +108 -55
- package/package.json +15 -15
- package/schema.json +1 -1
package/lib/runtime.js
CHANGED
|
@@ -2023,41 +2023,47 @@ export class Runtime extends EventEmitter {
|
|
|
2023
2023
|
return
|
|
2024
2024
|
}
|
|
2025
2025
|
|
|
2026
|
-
//
|
|
2026
|
+
// Collect health from all workers in parallel so that a slow ITC
|
|
2027
|
+
// round-trip (e.g. subprocess timeout) does not block every other worker.
|
|
2028
|
+
const pending = []
|
|
2027
2029
|
for (const worker of this.#workers.values()) {
|
|
2028
2030
|
if (worker[kWorkerStatus] !== 'started') {
|
|
2029
2031
|
continue
|
|
2030
2032
|
}
|
|
2031
2033
|
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2034
|
+
pending.push((async () => {
|
|
2035
|
+
const id = worker[kApplicationId]
|
|
2036
|
+
const index = worker[kWorkerId]
|
|
2037
|
+
const errorLabel = this.#workerExtendedLabel(id, index, worker[kConfig].workers)
|
|
2035
2038
|
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
const healthSignals = worker[kWorkerHealthSignals]?.getAll() ?? []
|
|
2039
|
+
let health = null
|
|
2040
|
+
try {
|
|
2041
|
+
health = await this.getWorkerHealth(worker, {
|
|
2042
|
+
previousELU: worker[kLastHealthCheckELU]
|
|
2043
|
+
})
|
|
2044
|
+
} catch (err) {
|
|
2045
|
+
this.logger.error({ err }, `Failed to get health for ${errorLabel}.`)
|
|
2046
|
+
} finally {
|
|
2047
|
+
worker[kLastHealthCheckELU] = health?.currentELU ?? null
|
|
2048
|
+
}
|
|
2048
2049
|
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
application:
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2050
|
+
const healthSignals = worker[kWorkerHealthSignals]?.getAll() ?? []
|
|
2051
|
+
|
|
2052
|
+
// We use emit instead of emitAndNotify to avoid sending a postMessages
|
|
2053
|
+
// to each workers even if they are not interested in health metrics.
|
|
2054
|
+
// No one of the known capabilities use this event yet.
|
|
2055
|
+
this.emit('application:worker:health:metrics', {
|
|
2056
|
+
id: worker[kId],
|
|
2057
|
+
application: id,
|
|
2058
|
+
worker: index,
|
|
2059
|
+
currentHealth: health,
|
|
2060
|
+
healthSignals
|
|
2061
|
+
})
|
|
2062
|
+
})())
|
|
2059
2063
|
}
|
|
2060
2064
|
|
|
2065
|
+
await Promise.allSettled(pending)
|
|
2066
|
+
|
|
2061
2067
|
// Reschedule the next check. We are not using .refresh() because it's more
|
|
2062
2068
|
// expensive (weird).
|
|
2063
2069
|
this.#healthMetricsTimer = setTimeout(collectHealthMetrics, 1000).unref()
|
|
@@ -2109,53 +2115,45 @@ export class Runtime extends EventEmitter {
|
|
|
2109
2115
|
worker[kHealthCheckTimer] = setTimeout(async () => {
|
|
2110
2116
|
if (worker[kWorkerStatus] !== 'started') return
|
|
2111
2117
|
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2118
|
+
// No health data received yet — reschedule and wait.
|
|
2119
|
+
if (!lastHealthMetrics) {
|
|
2120
|
+
worker[kHealthCheckTimer].refresh()
|
|
2121
|
+
return
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2124
|
+
const health = lastHealthMetrics.currentHealth
|
|
2125
|
+
|
|
2126
|
+
// When health collection failed (threw) or timed out, currentHealth is
|
|
2127
|
+
// null. Treat this as an unhealthy check so that a stuck worker that
|
|
2128
|
+
// cannot even report its own health is eventually replaced.
|
|
2129
|
+
if (!health) {
|
|
2130
|
+
unhealthyChecks++
|
|
2131
|
+
|
|
2132
|
+
this.logger.error(
|
|
2133
|
+
`Health collection failed for the ${errorLabel}. ` +
|
|
2134
|
+
`Unhealthy check ${unhealthyChecks}/${maxUnhealthyChecks}.`
|
|
2135
|
+
)
|
|
2116
2136
|
|
|
2117
2137
|
this.emit('application:worker:health', {
|
|
2118
2138
|
id: worker[kId],
|
|
2119
2139
|
application: id,
|
|
2120
2140
|
worker: index,
|
|
2121
|
-
currentHealth:
|
|
2122
|
-
unhealthy,
|
|
2141
|
+
currentHealth: null,
|
|
2142
|
+
unhealthy: true,
|
|
2123
2143
|
healthConfig
|
|
2124
2144
|
})
|
|
2125
2145
|
|
|
2126
|
-
if (health.elu > maxELU) {
|
|
2127
|
-
this.logger.error(
|
|
2128
|
-
`The ${errorLabel} has an ELU of ${(health.elu * 100).toFixed(2)} %, ` +
|
|
2129
|
-
`above the maximum allowed usage of ${(maxELU * 100).toFixed(2)} %.`
|
|
2130
|
-
)
|
|
2131
|
-
}
|
|
2132
|
-
|
|
2133
|
-
if (memoryUsage > maxHeapUsed) {
|
|
2134
|
-
this.logger.error(
|
|
2135
|
-
`The ${errorLabel} is using ${(memoryUsage * 100).toFixed(2)} % of the memory, ` +
|
|
2136
|
-
`above the maximum allowed usage of ${(maxHeapUsed * 100).toFixed(2)} %.`
|
|
2137
|
-
)
|
|
2138
|
-
}
|
|
2139
|
-
|
|
2140
|
-
if (unhealthy) {
|
|
2141
|
-
unhealthyChecks++
|
|
2142
|
-
} else {
|
|
2143
|
-
unhealthyChecks = 0
|
|
2144
|
-
}
|
|
2145
|
-
|
|
2146
2146
|
if (unhealthyChecks === maxUnhealthyChecks) {
|
|
2147
2147
|
try {
|
|
2148
2148
|
this.emitAndNotify('application:worker:unhealthy', { application: id, worker: index })
|
|
2149
2149
|
|
|
2150
2150
|
this.logger.error(
|
|
2151
|
-
{
|
|
2152
|
-
`The ${errorLabel} is unhealthy. Replacing it ...`
|
|
2151
|
+
`The ${errorLabel} is unhealthy (health collection failed). Replacing it ...`
|
|
2153
2152
|
)
|
|
2154
2153
|
|
|
2155
2154
|
await this.#replaceWorker(config, applicationConfig, workersCount, id, index, worker)
|
|
2156
2155
|
} catch (e) {
|
|
2157
2156
|
this.logger.error(
|
|
2158
|
-
{ elu: health.elu, maxELU, memoryUsage: health.heapUsed, maxMemoryUsage: maxHeapUsed },
|
|
2159
2157
|
`Cannot replace the ${errorLabel}. Forcefully terminating it ...`
|
|
2160
2158
|
)
|
|
2161
2159
|
|
|
@@ -2164,6 +2162,61 @@ export class Runtime extends EventEmitter {
|
|
|
2164
2162
|
} else {
|
|
2165
2163
|
worker[kHealthCheckTimer].refresh()
|
|
2166
2164
|
}
|
|
2165
|
+
return
|
|
2166
|
+
}
|
|
2167
|
+
|
|
2168
|
+
const memoryUsage = health.heapUsed != null ? health.heapUsed / maxHeapTotal : 0
|
|
2169
|
+
const unhealthy = health.elu > maxELU || memoryUsage > maxHeapUsed
|
|
2170
|
+
|
|
2171
|
+
this.emit('application:worker:health', {
|
|
2172
|
+
id: worker[kId],
|
|
2173
|
+
application: id,
|
|
2174
|
+
worker: index,
|
|
2175
|
+
currentHealth: health,
|
|
2176
|
+
unhealthy,
|
|
2177
|
+
healthConfig
|
|
2178
|
+
})
|
|
2179
|
+
|
|
2180
|
+
if (health.elu > maxELU) {
|
|
2181
|
+
this.logger.error(
|
|
2182
|
+
`The ${errorLabel} has an ELU of ${(health.elu * 100).toFixed(2)} %, ` +
|
|
2183
|
+
`above the maximum allowed usage of ${(maxELU * 100).toFixed(2)} %.`
|
|
2184
|
+
)
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
if (memoryUsage > maxHeapUsed) {
|
|
2188
|
+
this.logger.error(
|
|
2189
|
+
`The ${errorLabel} is using ${(memoryUsage * 100).toFixed(2)} % of the memory, ` +
|
|
2190
|
+
`above the maximum allowed usage of ${(maxHeapUsed * 100).toFixed(2)} %.`
|
|
2191
|
+
)
|
|
2192
|
+
}
|
|
2193
|
+
|
|
2194
|
+
if (unhealthy) {
|
|
2195
|
+
unhealthyChecks++
|
|
2196
|
+
} else {
|
|
2197
|
+
unhealthyChecks = 0
|
|
2198
|
+
}
|
|
2199
|
+
|
|
2200
|
+
if (unhealthyChecks === maxUnhealthyChecks) {
|
|
2201
|
+
try {
|
|
2202
|
+
this.emitAndNotify('application:worker:unhealthy', { application: id, worker: index })
|
|
2203
|
+
|
|
2204
|
+
this.logger.error(
|
|
2205
|
+
{ elu: health.elu, maxELU, memoryUsage: health.heapUsed, maxMemoryUsage: maxHeapUsed },
|
|
2206
|
+
`The ${errorLabel} is unhealthy. Replacing it ...`
|
|
2207
|
+
)
|
|
2208
|
+
|
|
2209
|
+
await this.#replaceWorker(config, applicationConfig, workersCount, id, index, worker)
|
|
2210
|
+
} catch (e) {
|
|
2211
|
+
this.logger.error(
|
|
2212
|
+
{ elu: health.elu, maxELU, memoryUsage: health.heapUsed, maxMemoryUsage: maxHeapUsed },
|
|
2213
|
+
`Cannot replace the ${errorLabel}. Forcefully terminating it ...`
|
|
2214
|
+
)
|
|
2215
|
+
|
|
2216
|
+
worker.terminate()
|
|
2217
|
+
}
|
|
2218
|
+
} else {
|
|
2219
|
+
worker[kHealthCheckTimer].refresh()
|
|
2167
2220
|
}
|
|
2168
2221
|
}, interval).unref()
|
|
2169
2222
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/runtime",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.53.0-alpha.0",
|
|
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.
|
|
40
|
-
"@platformatic/
|
|
41
|
-
"@platformatic/
|
|
42
|
-
"@platformatic/
|
|
43
|
-
"@platformatic/
|
|
44
|
-
"@platformatic/
|
|
45
|
-
"@platformatic/sql-
|
|
46
|
-
"@platformatic/wattpm-pprof-capture": "3.
|
|
39
|
+
"@platformatic/composer": "3.53.0-alpha.0",
|
|
40
|
+
"@platformatic/gateway": "3.53.0-alpha.0",
|
|
41
|
+
"@platformatic/db": "3.53.0-alpha.0",
|
|
42
|
+
"@platformatic/service": "3.53.0-alpha.0",
|
|
43
|
+
"@platformatic/sql-graphql": "3.53.0-alpha.0",
|
|
44
|
+
"@platformatic/node": "3.53.0-alpha.0",
|
|
45
|
+
"@platformatic/sql-mapper": "3.53.0-alpha.0",
|
|
46
|
+
"@platformatic/wattpm-pprof-capture": "3.53.0-alpha.0"
|
|
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/
|
|
77
|
-
"@platformatic/
|
|
78
|
-
"@platformatic/generators": "3.
|
|
79
|
-
"@platformatic/metrics": "3.
|
|
80
|
-
"@platformatic/
|
|
81
|
-
"@platformatic/
|
|
76
|
+
"@platformatic/foundation": "3.53.0-alpha.0",
|
|
77
|
+
"@platformatic/basic": "3.53.0-alpha.0",
|
|
78
|
+
"@platformatic/generators": "3.53.0-alpha.0",
|
|
79
|
+
"@platformatic/metrics": "3.53.0-alpha.0",
|
|
80
|
+
"@platformatic/itc": "3.53.0-alpha.0",
|
|
81
|
+
"@platformatic/telemetry": "3.53.0-alpha.0"
|
|
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.
|
|
2
|
+
"$id": "https://schemas.platformatic.dev/@platformatic/runtime/3.53.0-alpha.0.json",
|
|
3
3
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
4
|
"title": "Platformatic Runtime Config",
|
|
5
5
|
"type": "object",
|