@platformatic/runtime 3.17.0 → 3.19.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/config.d.ts +1 -0
- package/lib/errors.js +0 -4
- package/lib/management-api.js +22 -15
- package/lib/prom-server.js +4 -4
- package/lib/runtime.js +122 -90
- package/lib/worker/itc.js +24 -1
- package/lib/worker/symbols.js +0 -1
- package/package.json +15 -15
- package/schema.json +13 -3
package/config.d.ts
CHANGED
package/lib/errors.js
CHANGED
|
@@ -128,10 +128,6 @@ export const MissingPprofCapture = createError(
|
|
|
128
128
|
'Please install @platformatic/wattpm-pprof-capture'
|
|
129
129
|
)
|
|
130
130
|
|
|
131
|
-
export const GetHeapStatisticUnavailable = createError(
|
|
132
|
-
`${ERROR_PREFIX}_GET_HEAP_STATISTIC_UNAVAILABLE`,
|
|
133
|
-
'The getHeapStatistics method is not available in your Node version'
|
|
134
|
-
)
|
|
135
131
|
export const FailedToSendHealthSignalsError = createError(
|
|
136
132
|
`${ERROR_PREFIX}_FAILED_TO_SEND_HEALTH_SIGNALS`,
|
|
137
133
|
'Cannot send health signals from application "%s": %s'
|
package/lib/management-api.js
CHANGED
|
@@ -238,6 +238,7 @@ export async function managementApiPlugin (app, opts) {
|
|
|
238
238
|
return metrics
|
|
239
239
|
})
|
|
240
240
|
|
|
241
|
+
// TODO: Remove in next major version - deprecated endpoint
|
|
241
242
|
app.get('/metrics/live', { websocket: true }, async socket => {
|
|
242
243
|
const config = await runtime.getRuntimeConfig()
|
|
243
244
|
|
|
@@ -256,26 +257,32 @@ export async function managementApiPlugin (app, opts) {
|
|
|
256
257
|
return
|
|
257
258
|
}
|
|
258
259
|
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
260
|
+
const pollAndSendMetrics = async () => {
|
|
261
|
+
try {
|
|
262
|
+
const metrics = await runtime.getFormattedMetrics()
|
|
263
|
+
if (metrics) {
|
|
264
|
+
const serializedMetrics = JSON.stringify(metrics)
|
|
265
|
+
socket.send(serializedMetrics + '\n')
|
|
266
|
+
}
|
|
267
|
+
} catch (error) {
|
|
268
|
+
// If there's an error, stop polling and close the connection
|
|
269
|
+
clearInterval(pollingInterval)
|
|
270
|
+
socket.close()
|
|
271
|
+
}
|
|
263
272
|
}
|
|
264
273
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
socket.send(serializedMetrics + '\n')
|
|
268
|
-
}
|
|
274
|
+
// Poll every second
|
|
275
|
+
const pollingInterval = setInterval(pollAndSendMetrics, 1000)
|
|
269
276
|
|
|
270
|
-
|
|
277
|
+
// Send initial metrics immediately
|
|
278
|
+
await pollAndSendMetrics()
|
|
271
279
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
280
|
+
const cleanup = () => {
|
|
281
|
+
clearInterval(pollingInterval)
|
|
282
|
+
}
|
|
275
283
|
|
|
276
|
-
socket.on('
|
|
277
|
-
|
|
278
|
-
})
|
|
284
|
+
socket.on('error', cleanup)
|
|
285
|
+
socket.on('close', cleanup)
|
|
279
286
|
})
|
|
280
287
|
|
|
281
288
|
app.get('/logs/live', { websocket: true }, async socket => {
|
package/lib/prom-server.js
CHANGED
|
@@ -68,7 +68,7 @@ async function checkLiveness (runtime) {
|
|
|
68
68
|
return check
|
|
69
69
|
} else if (typeof check === 'object') {
|
|
70
70
|
response = check
|
|
71
|
-
return check.status
|
|
71
|
+
return check.status || false
|
|
72
72
|
}
|
|
73
73
|
return false
|
|
74
74
|
})
|
|
@@ -85,7 +85,7 @@ export async function startPrometheusServer (runtime, opts) {
|
|
|
85
85
|
const metricsEndpoint = opts.endpoint ?? DEFAULT_METRICS_ENDPOINT
|
|
86
86
|
const auth = opts.auth ?? null
|
|
87
87
|
|
|
88
|
-
const promServer = fastify({ name: 'Prometheus server' })
|
|
88
|
+
const promServer = fastify({ name: 'Prometheus server', loggerInstance: runtime.logger })
|
|
89
89
|
promServer.register(fastifyAccepts)
|
|
90
90
|
|
|
91
91
|
let onRequestHook
|
|
@@ -151,7 +151,7 @@ export async function startPrometheusServer (runtime, opts) {
|
|
|
151
151
|
url: readinessEndpoint,
|
|
152
152
|
method: 'GET',
|
|
153
153
|
logLevel: 'warn',
|
|
154
|
-
handler: async (
|
|
154
|
+
handler: async (_req, reply) => {
|
|
155
155
|
reply.type('text/plain')
|
|
156
156
|
|
|
157
157
|
const { status, response } = await checkReadiness(runtime)
|
|
@@ -190,7 +190,7 @@ export async function startPrometheusServer (runtime, opts) {
|
|
|
190
190
|
url: livenessEndpoint,
|
|
191
191
|
method: 'GET',
|
|
192
192
|
logLevel: 'warn',
|
|
193
|
-
handler: async (
|
|
193
|
+
handler: async (_req, reply) => {
|
|
194
194
|
reply.type('text/plain')
|
|
195
195
|
|
|
196
196
|
const { status, response, readiness } = await checkLiveness(runtime)
|
package/lib/runtime.js
CHANGED
|
@@ -31,13 +31,11 @@ import {
|
|
|
31
31
|
ApplicationNotStartedError,
|
|
32
32
|
ApplicationStartTimeoutError,
|
|
33
33
|
CannotRemoveEntrypointError,
|
|
34
|
-
GetHeapStatisticUnavailable,
|
|
35
34
|
InvalidArgumentError,
|
|
36
35
|
MessagingError,
|
|
37
36
|
MissingEntrypointError,
|
|
38
37
|
MissingPprofCapture,
|
|
39
38
|
RuntimeAbortedError,
|
|
40
|
-
RuntimeExitedError,
|
|
41
39
|
WorkerNotFoundError
|
|
42
40
|
} from './errors.js'
|
|
43
41
|
import { abstractLogger, createLogger } from './logger.js'
|
|
@@ -48,14 +46,13 @@ import { startScheduler } from './scheduler.js'
|
|
|
48
46
|
import { createSharedStore } from './shared-http-cache.js'
|
|
49
47
|
import { version } from './version.js'
|
|
50
48
|
import { HealthSignalsQueue } from './worker/health-signals.js'
|
|
51
|
-
import { sendViaITC, waitEventFromITC } from './worker/itc.js'
|
|
49
|
+
import { sendMultipleViaITC, sendViaITC, waitEventFromITC } from './worker/itc.js'
|
|
52
50
|
import { RoundRobinMap } from './worker/round-robin-map.js'
|
|
53
51
|
import {
|
|
54
52
|
kApplicationId,
|
|
55
53
|
kConfig,
|
|
56
54
|
kFullId,
|
|
57
55
|
kHealthCheckTimer,
|
|
58
|
-
kHealthMetricsTimer,
|
|
59
56
|
kId,
|
|
60
57
|
kITC,
|
|
61
58
|
kLastHealthCheckELU,
|
|
@@ -71,8 +68,6 @@ const kWorkerFile = join(import.meta.dirname, 'worker/main.js')
|
|
|
71
68
|
const kInspectorOptions = Symbol('plt.runtime.worker.inspectorOptions')
|
|
72
69
|
|
|
73
70
|
const MAX_LISTENERS_COUNT = 100
|
|
74
|
-
const MAX_METRICS_QUEUE_LENGTH = 5 * 60 // 5 minutes in seconds
|
|
75
|
-
const COLLECT_METRICS_TIMEOUT = 1000
|
|
76
71
|
|
|
77
72
|
const MAX_CONCURRENCY = 5
|
|
78
73
|
const MAX_BOOTSTRAP_ATTEMPTS = 5
|
|
@@ -98,8 +93,7 @@ export class Runtime extends EventEmitter {
|
|
|
98
93
|
#entrypointId
|
|
99
94
|
#url
|
|
100
95
|
|
|
101
|
-
#
|
|
102
|
-
#metricsTimeout
|
|
96
|
+
#healthMetricsTimer
|
|
103
97
|
|
|
104
98
|
#meshInterceptor
|
|
105
99
|
#dispatcher
|
|
@@ -283,9 +277,8 @@ export class Runtime extends EventEmitter {
|
|
|
283
277
|
|
|
284
278
|
this.#updateStatus('started')
|
|
285
279
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
}
|
|
280
|
+
// Start the global health metrics timer for all workers if needed
|
|
281
|
+
this.#startHealthMetricsCollectionIfNeeded()
|
|
289
282
|
|
|
290
283
|
await this.#dynamicWorkersScaler?.start()
|
|
291
284
|
this.#showUrl()
|
|
@@ -339,7 +332,7 @@ export class Runtime extends EventEmitter {
|
|
|
339
332
|
}
|
|
340
333
|
|
|
341
334
|
async close (silent = false) {
|
|
342
|
-
|
|
335
|
+
clearTimeout(this.#healthMetricsTimer)
|
|
343
336
|
|
|
344
337
|
await this.stop(silent)
|
|
345
338
|
this.#updateStatus('closing')
|
|
@@ -703,29 +696,15 @@ export class Runtime extends EventEmitter {
|
|
|
703
696
|
}
|
|
704
697
|
}
|
|
705
698
|
|
|
699
|
+
// TODO: Remove in next major version
|
|
706
700
|
startCollectingMetrics () {
|
|
707
|
-
this
|
|
708
|
-
|
|
709
|
-
if (this.#status !== 'started') {
|
|
710
|
-
return
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
let metrics = null
|
|
714
|
-
try {
|
|
715
|
-
metrics = await this.getFormattedMetrics()
|
|
716
|
-
} catch (error) {
|
|
717
|
-
if (!(error instanceof RuntimeExitedError)) {
|
|
718
|
-
this.logger.error({ err: ensureLoggableError(error) }, 'Error collecting metrics')
|
|
719
|
-
}
|
|
720
|
-
return
|
|
721
|
-
}
|
|
701
|
+
this.logger.warn('startCollectingMetrics() is deprecated and no longer collects metrics. Metrics are now polled on-demand by the management API.')
|
|
702
|
+
}
|
|
722
703
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
}
|
|
728
|
-
}, COLLECT_METRICS_TIMEOUT).unref()
|
|
704
|
+
// TODO: Remove in next major version
|
|
705
|
+
getCachedMetrics () {
|
|
706
|
+
this.logger.warn('getCachedMetrics() is deprecated and returns an empty array. Metrics are no longer cached.')
|
|
707
|
+
return []
|
|
729
708
|
}
|
|
730
709
|
|
|
731
710
|
invalidateHttpCache (options = {}) {
|
|
@@ -966,31 +945,45 @@ export class Runtime extends EventEmitter {
|
|
|
966
945
|
}
|
|
967
946
|
|
|
968
947
|
async getCustomHealthChecks () {
|
|
969
|
-
const
|
|
948
|
+
const invocations = []
|
|
970
949
|
|
|
971
950
|
for (const id of this.#applications.keys()) {
|
|
972
951
|
const workersIds = this.#workers.getKeys(id)
|
|
973
952
|
for (const workerId of workersIds) {
|
|
974
|
-
|
|
975
|
-
status[workerId] = await sendViaITC(worker, 'getCustomHealthCheck')
|
|
953
|
+
invocations.push([workerId, this.#workers.get(workerId)])
|
|
976
954
|
}
|
|
977
955
|
}
|
|
978
956
|
|
|
979
|
-
return
|
|
957
|
+
return sendMultipleViaITC(
|
|
958
|
+
invocations,
|
|
959
|
+
'getCustomHealthCheck',
|
|
960
|
+
undefined,
|
|
961
|
+
[],
|
|
962
|
+
this.#concurrency,
|
|
963
|
+
this.#config.metrics.healthChecksTimeout,
|
|
964
|
+
{}
|
|
965
|
+
)
|
|
980
966
|
}
|
|
981
967
|
|
|
982
968
|
async getCustomReadinessChecks () {
|
|
983
|
-
const
|
|
969
|
+
const invocations = []
|
|
984
970
|
|
|
985
971
|
for (const id of this.#applications.keys()) {
|
|
986
972
|
const workersIds = this.#workers.getKeys(id)
|
|
987
973
|
for (const workerId of workersIds) {
|
|
988
|
-
|
|
989
|
-
status[workerId] = await sendViaITC(worker, 'getCustomReadinessCheck')
|
|
974
|
+
invocations.push([workerId, this.#workers.get(workerId)])
|
|
990
975
|
}
|
|
991
976
|
}
|
|
992
977
|
|
|
993
|
-
return
|
|
978
|
+
return sendMultipleViaITC(
|
|
979
|
+
invocations,
|
|
980
|
+
'getCustomReadinessCheck',
|
|
981
|
+
undefined,
|
|
982
|
+
[],
|
|
983
|
+
this.#concurrency,
|
|
984
|
+
this.#config.metrics.healthChecksTimeout,
|
|
985
|
+
{}
|
|
986
|
+
)
|
|
994
987
|
}
|
|
995
988
|
|
|
996
989
|
async getMetrics (format = 'json') {
|
|
@@ -1036,10 +1029,6 @@ export class Runtime extends EventEmitter {
|
|
|
1036
1029
|
return { metrics }
|
|
1037
1030
|
}
|
|
1038
1031
|
|
|
1039
|
-
getCachedMetrics () {
|
|
1040
|
-
return this.#metrics
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
1032
|
async getFormattedMetrics () {
|
|
1044
1033
|
try {
|
|
1045
1034
|
const { metrics } = await this.getMetrics()
|
|
@@ -1280,10 +1269,6 @@ export class Runtime extends EventEmitter {
|
|
|
1280
1269
|
}
|
|
1281
1270
|
|
|
1282
1271
|
async getWorkerHealth (worker, options = {}) {
|
|
1283
|
-
if (!features.node.worker.getHeapStatistics) {
|
|
1284
|
-
throw new GetHeapStatisticUnavailable()
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
1272
|
const currentELU = worker.performance.eventLoopUtilization()
|
|
1288
1273
|
const previousELU = options.previousELU
|
|
1289
1274
|
|
|
@@ -1292,6 +1277,10 @@ export class Runtime extends EventEmitter {
|
|
|
1292
1277
|
elu = worker.performance.eventLoopUtilization(elu, previousELU)
|
|
1293
1278
|
}
|
|
1294
1279
|
|
|
1280
|
+
if (!features.node.worker.getHeapStatistics) {
|
|
1281
|
+
return { elu: elu.utilization, currentELU }
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1295
1284
|
const { used_heap_size: heapUsed, total_heap_size: heapTotal } = await worker.getHeapStatistics()
|
|
1296
1285
|
return { elu: elu.utilization, heapUsed, heapTotal, currentELU }
|
|
1297
1286
|
}
|
|
@@ -1460,17 +1449,26 @@ export class Runtime extends EventEmitter {
|
|
|
1460
1449
|
workerEnv.NODE_OPTIONS = `${originalNodeOptions} ${applicationConfig.nodeOptions}`.trim()
|
|
1461
1450
|
}
|
|
1462
1451
|
|
|
1463
|
-
|
|
1464
|
-
typeof health.maxHeapTotal === 'string' ? parseMemorySize(health.maxHeapTotal) : health.maxHeapTotal
|
|
1465
|
-
const maxYoungGeneration =
|
|
1466
|
-
typeof health.maxYoungGeneration === 'string'
|
|
1467
|
-
? parseMemorySize(health.maxYoungGeneration)
|
|
1468
|
-
: health.maxYoungGeneration
|
|
1452
|
+
let resourceLimits
|
|
1469
1453
|
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1454
|
+
{
|
|
1455
|
+
const maxHeapTotal =
|
|
1456
|
+
typeof health.maxHeapTotal === 'string' ? parseMemorySize(health.maxHeapTotal) : health.maxHeapTotal
|
|
1457
|
+
const maxYoungGeneration =
|
|
1458
|
+
typeof health.maxYoungGeneration === 'string'
|
|
1459
|
+
? parseMemorySize(health.maxYoungGeneration)
|
|
1460
|
+
: health.maxYoungGeneration
|
|
1461
|
+
|
|
1462
|
+
const maxOldGenerationSizeMb = maxHeapTotal ? Math.floor((maxYoungGeneration > 0 ? maxHeapTotal - maxYoungGeneration : maxHeapTotal) / (1024 * 1024)) : undefined
|
|
1463
|
+
const maxYoungGenerationSizeMb = maxYoungGeneration ? Math.floor(maxYoungGeneration / (1024 * 1024)) : undefined
|
|
1464
|
+
|
|
1465
|
+
if (maxOldGenerationSizeMb || maxYoungGenerationSizeMb) {
|
|
1466
|
+
resourceLimits = {
|
|
1467
|
+
maxOldGenerationSizeMb,
|
|
1468
|
+
maxYoungGenerationSizeMb
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1474
1472
|
|
|
1475
1473
|
const worker = new Worker(kWorkerFile, {
|
|
1476
1474
|
workerData: {
|
|
@@ -1494,10 +1492,7 @@ export class Runtime extends EventEmitter {
|
|
|
1494
1492
|
argv: applicationConfig.arguments,
|
|
1495
1493
|
execArgv,
|
|
1496
1494
|
env: workerEnv,
|
|
1497
|
-
resourceLimits
|
|
1498
|
-
maxOldGenerationSizeMb,
|
|
1499
|
-
maxYoungGenerationSizeMb
|
|
1500
|
-
},
|
|
1495
|
+
resourceLimits,
|
|
1501
1496
|
stdout: true,
|
|
1502
1497
|
stderr: true
|
|
1503
1498
|
})
|
|
@@ -1646,36 +1641,75 @@ export class Runtime extends EventEmitter {
|
|
|
1646
1641
|
return worker
|
|
1647
1642
|
}
|
|
1648
1643
|
|
|
1649
|
-
#
|
|
1650
|
-
//
|
|
1651
|
-
worker
|
|
1644
|
+
#startHealthMetricsCollectionIfNeeded () {
|
|
1645
|
+
// Need health metrics if dynamic workers scaler exists (for vertical scaling)
|
|
1646
|
+
// or if any worker has health checks enabled
|
|
1647
|
+
let needsHealthMetrics = !!this.#dynamicWorkersScaler
|
|
1652
1648
|
|
|
1653
|
-
|
|
1654
|
-
if
|
|
1649
|
+
if (!needsHealthMetrics) {
|
|
1650
|
+
// Check if any worker has health checks enabled
|
|
1651
|
+
for (const worker of this.#workers.values()) {
|
|
1652
|
+
const healthConfig = worker[kConfig]?.health
|
|
1653
|
+
if (healthConfig?.enabled && this.#config.restartOnError > 0) {
|
|
1654
|
+
needsHealthMetrics = true
|
|
1655
|
+
break
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1655
1659
|
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1660
|
+
if (needsHealthMetrics) {
|
|
1661
|
+
this.#startHealthMetricsCollection()
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
#startHealthMetricsCollection () {
|
|
1666
|
+
const collectHealthMetrics = async () => {
|
|
1667
|
+
if (this.#status !== 'started') {
|
|
1668
|
+
return
|
|
1665
1669
|
}
|
|
1666
1670
|
|
|
1667
|
-
|
|
1671
|
+
// Iterate through all workers and collect health metrics
|
|
1672
|
+
for (const worker of this.#workers.values()) {
|
|
1673
|
+
if (worker[kWorkerStatus] !== 'started') {
|
|
1674
|
+
continue
|
|
1675
|
+
}
|
|
1668
1676
|
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
worker: index,
|
|
1673
|
-
currentHealth: health,
|
|
1674
|
-
healthSignals
|
|
1675
|
-
})
|
|
1677
|
+
const id = worker[kApplicationId]
|
|
1678
|
+
const index = worker[kWorkerId]
|
|
1679
|
+
const errorLabel = this.#workerExtendedLabel(id, index, worker[kConfig].workers)
|
|
1676
1680
|
|
|
1677
|
-
|
|
1678
|
-
|
|
1681
|
+
let health = null
|
|
1682
|
+
try {
|
|
1683
|
+
health = await this.getWorkerHealth(worker, {
|
|
1684
|
+
previousELU: worker[kLastHealthCheckELU]
|
|
1685
|
+
})
|
|
1686
|
+
} catch (err) {
|
|
1687
|
+
this.logger.error({ err }, `Failed to get health for ${errorLabel}.`)
|
|
1688
|
+
} finally {
|
|
1689
|
+
worker[kLastHealthCheckELU] = health?.currentELU ?? null
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
const healthSignals = worker[kWorkerHealthSignals]?.getAll() ?? []
|
|
1693
|
+
|
|
1694
|
+
// We use emit instead of emitAndNotify to avoid sending a postMessages
|
|
1695
|
+
// to each workers even if they are not interested in health metrics.
|
|
1696
|
+
// No one of the known capabilities use this event yet.
|
|
1697
|
+
this.emit('application:worker:health:metrics', {
|
|
1698
|
+
id: worker[kId],
|
|
1699
|
+
application: id,
|
|
1700
|
+
worker: index,
|
|
1701
|
+
currentHealth: health,
|
|
1702
|
+
healthSignals
|
|
1703
|
+
})
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
// Reschedule the next check. We are not using .refresh() because it's more
|
|
1707
|
+
// expensive (weird).
|
|
1708
|
+
this.#healthMetricsTimer = setTimeout(collectHealthMetrics, 1000).unref()
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
// Start the collection
|
|
1712
|
+
this.#healthMetricsTimer = setTimeout(collectHealthMetrics, 1000).unref()
|
|
1679
1713
|
}
|
|
1680
1714
|
|
|
1681
1715
|
#setupHealthCheck (config, applicationConfig, workersCount, id, index, worker, errorLabel) {
|
|
@@ -1725,7 +1759,7 @@ export class Runtime extends EventEmitter {
|
|
|
1725
1759
|
const memoryUsage = health.heapUsed / maxHeapTotal
|
|
1726
1760
|
const unhealthy = health.elu > maxELU || memoryUsage > maxHeapUsed
|
|
1727
1761
|
|
|
1728
|
-
this.
|
|
1762
|
+
this.emit('application:worker:health', {
|
|
1729
1763
|
id: worker[kId],
|
|
1730
1764
|
application: id,
|
|
1731
1765
|
worker: index,
|
|
@@ -1842,8 +1876,6 @@ export class Runtime extends EventEmitter {
|
|
|
1842
1876
|
this.logger.info(`Started the ${label}...`)
|
|
1843
1877
|
}
|
|
1844
1878
|
|
|
1845
|
-
this.#setupHealthMetrics(id, index, worker, label)
|
|
1846
|
-
|
|
1847
1879
|
const { enabled, gracePeriod } = worker[kConfig].health
|
|
1848
1880
|
if (enabled && config.restartOnError > 0) {
|
|
1849
1881
|
// if gracePeriod is 0, it will be set to 1 to start health checks immediately
|
package/lib/worker/itc.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ensureLoggableError } from '@platformatic/foundation'
|
|
1
|
+
import { ensureLoggableError, executeInParallel, executeWithTimeout, kTimeout } from '@platformatic/foundation'
|
|
2
2
|
import { ITC } from '@platformatic/itc'
|
|
3
3
|
import { Unpromise } from '@watchable/unpromise'
|
|
4
4
|
import { once } from 'node:events'
|
|
@@ -65,6 +65,29 @@ export async function sendViaITC (worker, name, message, transferList) {
|
|
|
65
65
|
return safeHandleInITC(worker, () => worker[kITC].send(name, message, { transferList }))
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
export async function sendMultipleViaITC (
|
|
69
|
+
idsAndWorkerPairs,
|
|
70
|
+
name,
|
|
71
|
+
message,
|
|
72
|
+
transferList,
|
|
73
|
+
concurrency,
|
|
74
|
+
timeout = 5000,
|
|
75
|
+
timeoutFallbackValue = kTimeout
|
|
76
|
+
) {
|
|
77
|
+
const results = await executeInParallel(
|
|
78
|
+
async (id, worker) => {
|
|
79
|
+
return [
|
|
80
|
+
id,
|
|
81
|
+
await executeWithTimeout(sendViaITC(worker, name, message, transferList), timeout, timeoutFallbackValue)
|
|
82
|
+
]
|
|
83
|
+
},
|
|
84
|
+
idsAndWorkerPairs,
|
|
85
|
+
concurrency
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
return Object.fromEntries(results)
|
|
89
|
+
}
|
|
90
|
+
|
|
68
91
|
export async function waitEventFromITC (worker, event) {
|
|
69
92
|
return safeHandleInITC(worker, () => once(worker[kITC], event))
|
|
70
93
|
}
|
package/lib/worker/symbols.js
CHANGED
|
@@ -5,7 +5,6 @@ export const kApplicationId = Symbol.for('plt.runtime.application.id')
|
|
|
5
5
|
export const kWorkerId = Symbol.for('plt.runtime.worker.id')
|
|
6
6
|
export const kITC = Symbol.for('plt.runtime.itc')
|
|
7
7
|
export const kHealthCheckTimer = Symbol.for('plt.runtime.worker.healthCheckTimer')
|
|
8
|
-
export const kHealthMetricsTimer = Symbol.for('plt.runtime.worker.healthMetricsTimer')
|
|
9
8
|
export const kWorkerStatus = Symbol('plt.runtime.worker.status')
|
|
10
9
|
export const kWorkerHealthSignals = Symbol.for('plt.runtime.worker.healthSignals')
|
|
11
10
|
export const kWorkerStartTime = Symbol.for('plt.runtime.worker.startTime')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/runtime",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.19.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -35,14 +35,14 @@
|
|
|
35
35
|
"typescript": "^5.5.4",
|
|
36
36
|
"undici-oidc-interceptor": "^0.5.0",
|
|
37
37
|
"why-is-node-running": "^2.2.2",
|
|
38
|
-
"@platformatic/
|
|
39
|
-
"@platformatic/
|
|
40
|
-
"@platformatic/gateway": "3.
|
|
41
|
-
"@platformatic/node": "3.
|
|
42
|
-
"@platformatic/service": "3.
|
|
43
|
-
"@platformatic/sql-
|
|
44
|
-
"@platformatic/sql-
|
|
45
|
-
"@platformatic/wattpm-pprof-capture": "3.
|
|
38
|
+
"@platformatic/composer": "3.19.0",
|
|
39
|
+
"@platformatic/db": "3.19.0",
|
|
40
|
+
"@platformatic/gateway": "3.19.0",
|
|
41
|
+
"@platformatic/node": "3.19.0",
|
|
42
|
+
"@platformatic/service": "3.19.0",
|
|
43
|
+
"@platformatic/sql-mapper": "3.19.0",
|
|
44
|
+
"@platformatic/sql-graphql": "3.19.0",
|
|
45
|
+
"@platformatic/wattpm-pprof-capture": "3.19.0"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@fastify/accepts": "^5.0.0",
|
|
@@ -71,12 +71,12 @@
|
|
|
71
71
|
"undici": "^7.0.0",
|
|
72
72
|
"undici-thread-interceptor": "^0.15.0",
|
|
73
73
|
"ws": "^8.16.0",
|
|
74
|
-
"@platformatic/basic": "3.
|
|
75
|
-
"@platformatic/
|
|
76
|
-
"@platformatic/
|
|
77
|
-
"@platformatic/
|
|
78
|
-
"@platformatic/
|
|
79
|
-
"@platformatic/
|
|
74
|
+
"@platformatic/basic": "3.19.0",
|
|
75
|
+
"@platformatic/generators": "3.19.0",
|
|
76
|
+
"@platformatic/foundation": "3.19.0",
|
|
77
|
+
"@platformatic/telemetry": "3.19.0",
|
|
78
|
+
"@platformatic/itc": "3.19.0",
|
|
79
|
+
"@platformatic/metrics": "3.19.0"
|
|
80
80
|
},
|
|
81
81
|
"engines": {
|
|
82
82
|
"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.19.0.json",
|
|
3
3
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
4
|
"title": "Platformatic Runtime Config",
|
|
5
5
|
"type": "object",
|
|
@@ -1626,8 +1626,7 @@
|
|
|
1626
1626
|
{
|
|
1627
1627
|
"type": "string"
|
|
1628
1628
|
}
|
|
1629
|
-
]
|
|
1630
|
-
"default": 4294967296
|
|
1629
|
+
]
|
|
1631
1630
|
},
|
|
1632
1631
|
"maxYoungGeneration": {
|
|
1633
1632
|
"anyOf": [
|
|
@@ -1954,6 +1953,17 @@
|
|
|
1954
1953
|
}
|
|
1955
1954
|
]
|
|
1956
1955
|
},
|
|
1956
|
+
"healthChecksTimeouts": {
|
|
1957
|
+
"anyOf": [
|
|
1958
|
+
{
|
|
1959
|
+
"type": "integer"
|
|
1960
|
+
},
|
|
1961
|
+
{
|
|
1962
|
+
"type": "string"
|
|
1963
|
+
}
|
|
1964
|
+
],
|
|
1965
|
+
"default": 5000
|
|
1966
|
+
},
|
|
1957
1967
|
"plugins": {
|
|
1958
1968
|
"type": "array",
|
|
1959
1969
|
"items": {
|