@platformatic/runtime 2.42.0 → 2.44.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 -1
- package/lib/config.js +20 -11
- package/lib/runtime.js +131 -22
- package/lib/schema.js +1 -1
- package/package.json +14 -14
- package/schema.json +14 -14
package/config.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* and run json-schema-to-typescript to regenerate this file.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
export type
|
|
8
|
+
export type HttpsSchemasPlatformaticDevPlatformaticRuntime2440Json = {
|
|
9
9
|
[k: string]: unknown;
|
|
10
10
|
} & {
|
|
11
11
|
$schema?: string;
|
package/lib/config.js
CHANGED
|
@@ -15,6 +15,13 @@ const { parseArgs } = require('node:util')
|
|
|
15
15
|
async function _transformConfig (configManager, args) {
|
|
16
16
|
const config = configManager.current
|
|
17
17
|
|
|
18
|
+
const { values } = parseArgs({
|
|
19
|
+
args,
|
|
20
|
+
strict: false,
|
|
21
|
+
options: { production: { type: 'boolean', short: 'p', default: false } }
|
|
22
|
+
})
|
|
23
|
+
const production = values.production
|
|
24
|
+
|
|
18
25
|
let services
|
|
19
26
|
if (config.web?.length) {
|
|
20
27
|
if (config.services?.length) {
|
|
@@ -30,13 +37,7 @@ async function _transformConfig (configManager, args) {
|
|
|
30
37
|
if (watchType === 'string') {
|
|
31
38
|
config.watch = config.watch === 'true'
|
|
32
39
|
} else if (watchType === 'undefined') {
|
|
33
|
-
|
|
34
|
-
args,
|
|
35
|
-
strict: false,
|
|
36
|
-
options: { production: { type: 'boolean', short: 'p', default: false } }
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
config.watch = !values.production
|
|
40
|
+
config.watch = !production
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
if (config.autoload) {
|
|
@@ -109,7 +110,7 @@ async function _transformConfig (configManager, args) {
|
|
|
109
110
|
// https://github.com/rust-lang/rust/issues/91979
|
|
110
111
|
// https://github.com/rollup/rollup/issues/5761
|
|
111
112
|
// TODO(mcollina): we should expose this inside every stackable configuration.
|
|
112
|
-
serviceConfig.app.modulesToLoad?.forEach(
|
|
113
|
+
serviceConfig.app.modulesToLoad?.forEach(m => {
|
|
113
114
|
const toLoad = _require.resolve(m)
|
|
114
115
|
loadModule(_require, toLoad).catch(() => {})
|
|
115
116
|
})
|
|
@@ -142,7 +143,7 @@ async function _transformConfig (configManager, args) {
|
|
|
142
143
|
// https://github.com/rust-lang/rust/issues/91979
|
|
143
144
|
// https://github.com/rollup/rollup/issues/5761
|
|
144
145
|
// TODO(mcollina): we should expose this inside every stackable configuration.
|
|
145
|
-
imported.stackable.default.modulesToLoad?.forEach(
|
|
146
|
+
imported.stackable.default.modulesToLoad?.forEach(m => {
|
|
146
147
|
const toLoad = _require.resolve(m)
|
|
147
148
|
loadModule(_require, toLoad).catch(() => {})
|
|
148
149
|
})
|
|
@@ -210,8 +211,16 @@ async function _transformConfig (configManager, args) {
|
|
|
210
211
|
configManager.current.services = services
|
|
211
212
|
configManager.current.web = undefined
|
|
212
213
|
|
|
213
|
-
if (
|
|
214
|
-
|
|
214
|
+
if (production) {
|
|
215
|
+
// Any value below 10 is considered as "immediate restart" and won't be processed via setTimeout or similar
|
|
216
|
+
// Important: do not use 2 otherwise ajv will convert to boolean `true`
|
|
217
|
+
configManager.current.restartOnError = 2
|
|
218
|
+
} else {
|
|
219
|
+
if (configManager.current.restartOnError === true) {
|
|
220
|
+
configManager.current.restartOnError = 5000
|
|
221
|
+
} else if (configManager.current.restartOnError < 0) {
|
|
222
|
+
configManager.current.restartOnError = 0
|
|
223
|
+
}
|
|
215
224
|
}
|
|
216
225
|
}
|
|
217
226
|
|
package/lib/runtime.js
CHANGED
|
@@ -41,12 +41,14 @@ const platformaticVersion = require('../package.json').version
|
|
|
41
41
|
const kWorkerFile = join(__dirname, 'worker/main.js')
|
|
42
42
|
|
|
43
43
|
const kInspectorOptions = Symbol('plt.runtime.worker.inspectorOptions')
|
|
44
|
+
const kForwardEvents = Symbol('plt.runtime.worker.forwardEvents')
|
|
44
45
|
|
|
45
46
|
const MAX_LISTENERS_COUNT = 100
|
|
46
47
|
const MAX_METRICS_QUEUE_LENGTH = 5 * 60 // 5 minutes in seconds
|
|
47
48
|
const COLLECT_METRICS_TIMEOUT = 1000
|
|
48
49
|
|
|
49
50
|
const MAX_BOOTSTRAP_ATTEMPTS = 5
|
|
51
|
+
const IMMEDIATE_RESTART_MAX_THRESHOLD = 10
|
|
50
52
|
|
|
51
53
|
const telemetryPath = require.resolve('@platformatic/telemetry')
|
|
52
54
|
const openTelemetrySetupPath = join(telemetryPath, '..', 'lib', 'node-telemetry.js')
|
|
@@ -142,7 +144,7 @@ class Runtime extends EventEmitter {
|
|
|
142
144
|
serviceConfig.id
|
|
143
145
|
)
|
|
144
146
|
|
|
145
|
-
|
|
147
|
+
await this.closeAndThrow(new errors.RuntimeAbortedError())
|
|
146
148
|
}
|
|
147
149
|
} else {
|
|
148
150
|
this.logger.error(
|
|
@@ -150,7 +152,7 @@ class Runtime extends EventEmitter {
|
|
|
150
152
|
serviceConfig.id
|
|
151
153
|
)
|
|
152
154
|
|
|
153
|
-
|
|
155
|
+
await this.closeAndThrow(new errors.RuntimeAbortedError())
|
|
154
156
|
}
|
|
155
157
|
}
|
|
156
158
|
|
|
@@ -183,8 +185,7 @@ class Runtime extends EventEmitter {
|
|
|
183
185
|
}
|
|
184
186
|
}
|
|
185
187
|
} catch (e) {
|
|
186
|
-
await this.
|
|
187
|
-
throw e
|
|
188
|
+
await this.closeAndThrow(e)
|
|
188
189
|
}
|
|
189
190
|
|
|
190
191
|
const dispatcherOpts = { ...config.undici }
|
|
@@ -249,10 +250,7 @@ class Runtime extends EventEmitter {
|
|
|
249
250
|
this.#inspectorServer = server
|
|
250
251
|
}
|
|
251
252
|
} catch (error) {
|
|
252
|
-
|
|
253
|
-
await sleep(1)
|
|
254
|
-
await this.close()
|
|
255
|
-
throw error
|
|
253
|
+
await this.closeAndThrow(error)
|
|
256
254
|
}
|
|
257
255
|
|
|
258
256
|
this.#updateStatus('started')
|
|
@@ -334,6 +332,16 @@ class Runtime extends EventEmitter {
|
|
|
334
332
|
this.#updateStatus('closed')
|
|
335
333
|
}
|
|
336
334
|
|
|
335
|
+
async closeAndThrow (error) {
|
|
336
|
+
this.#updateStatus('errored', error)
|
|
337
|
+
|
|
338
|
+
// Wait for the next tick so that any pending logging is properly flushed
|
|
339
|
+
await sleep(1)
|
|
340
|
+
await this.close()
|
|
341
|
+
|
|
342
|
+
throw error
|
|
343
|
+
}
|
|
344
|
+
|
|
337
345
|
async startService (id, silent = false) {
|
|
338
346
|
// Since when a service is stopped the worker is deleted, we consider a service start if its first service
|
|
339
347
|
// is no longer in the init phase
|
|
@@ -351,9 +359,13 @@ class Runtime extends EventEmitter {
|
|
|
351
359
|
|
|
352
360
|
const workersCount = await this.#workers.getCount(serviceConfig.id)
|
|
353
361
|
|
|
362
|
+
this.emit('service:starting', id)
|
|
363
|
+
|
|
354
364
|
for (let i = 0; i < workersCount; i++) {
|
|
355
365
|
await this.#startWorker(config, serviceConfig, workersCount, id, i, silent)
|
|
356
366
|
}
|
|
367
|
+
|
|
368
|
+
this.emit('service:started', id)
|
|
357
369
|
}
|
|
358
370
|
|
|
359
371
|
async stopService (id, silent = false) {
|
|
@@ -366,16 +378,22 @@ class Runtime extends EventEmitter {
|
|
|
366
378
|
|
|
367
379
|
const workersCount = await this.#workers.getCount(serviceConfig.id)
|
|
368
380
|
|
|
381
|
+
this.emit('service:stopping', id)
|
|
382
|
+
|
|
369
383
|
for (let i = 0; i < workersCount; i++) {
|
|
370
384
|
await this.#stopWorker(workersCount, id, i, silent)
|
|
371
385
|
}
|
|
386
|
+
|
|
387
|
+
this.emit('service:stopped', id)
|
|
372
388
|
}
|
|
373
389
|
|
|
374
390
|
async buildService (id) {
|
|
375
391
|
const service = await this.#getServiceById(id)
|
|
376
392
|
|
|
393
|
+
this.emit('service:building', id)
|
|
377
394
|
try {
|
|
378
|
-
|
|
395
|
+
await sendViaITC(service, 'build')
|
|
396
|
+
this.emit('service:built', id)
|
|
379
397
|
} catch (e) {
|
|
380
398
|
// The service exports no meta, return an empty object
|
|
381
399
|
if (e.code === 'PLT_ITC_HANDLER_NOT_FOUND') {
|
|
@@ -600,6 +618,26 @@ class Runtime extends EventEmitter {
|
|
|
600
618
|
}
|
|
601
619
|
}
|
|
602
620
|
|
|
621
|
+
async getWorkers () {
|
|
622
|
+
const status = {}
|
|
623
|
+
|
|
624
|
+
for (const [service, { count }] of Object.entries(this.#workers.configuration)) {
|
|
625
|
+
for (let i = 0; i < count; i++) {
|
|
626
|
+
const label = `${service}:${i}`
|
|
627
|
+
const worker = this.#workers.get(label)
|
|
628
|
+
|
|
629
|
+
status[label] = {
|
|
630
|
+
service,
|
|
631
|
+
worker: i,
|
|
632
|
+
status: worker?.[kWorkerStatus] ?? 'exited',
|
|
633
|
+
thread: worker?.threadId
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
return status
|
|
639
|
+
}
|
|
640
|
+
|
|
603
641
|
async getServiceDetails (id, allowUnloaded = false) {
|
|
604
642
|
let service
|
|
605
643
|
|
|
@@ -935,9 +973,19 @@ class Runtime extends EventEmitter {
|
|
|
935
973
|
}
|
|
936
974
|
}
|
|
937
975
|
|
|
938
|
-
|
|
976
|
+
emit (event, payload) {
|
|
977
|
+
for (const worker of this.#workers.values()) {
|
|
978
|
+
if (worker[kForwardEvents]) {
|
|
979
|
+
worker[kITC].notify('runtime:event', { event, payload })
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
return super.emit(event, payload)
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
#updateStatus (status, args) {
|
|
939
987
|
this.#status = status
|
|
940
|
-
this.emit(status)
|
|
988
|
+
this.emit(status, args)
|
|
941
989
|
}
|
|
942
990
|
|
|
943
991
|
#showUrl () {
|
|
@@ -954,6 +1002,8 @@ class Runtime extends EventEmitter {
|
|
|
954
1002
|
for (let i = 0; i < workersCount; i++) {
|
|
955
1003
|
await this.#setupWorker(config, serviceConfig, workersCount, id, i)
|
|
956
1004
|
}
|
|
1005
|
+
|
|
1006
|
+
this.emit('service:init', id)
|
|
957
1007
|
}
|
|
958
1008
|
|
|
959
1009
|
async #setupWorker (config, serviceConfig, workersCount, serviceId, index) {
|
|
@@ -1036,6 +1086,7 @@ class Runtime extends EventEmitter {
|
|
|
1036
1086
|
worker.setMaxListeners(1e3)
|
|
1037
1087
|
|
|
1038
1088
|
// Track service exiting
|
|
1089
|
+
const eventPayload = { service: serviceId, worker: index, workersCount }
|
|
1039
1090
|
worker.once('exit', code => {
|
|
1040
1091
|
if (worker[kWorkerStatus] === 'exited') {
|
|
1041
1092
|
return
|
|
@@ -1043,6 +1094,7 @@ class Runtime extends EventEmitter {
|
|
|
1043
1094
|
|
|
1044
1095
|
const started = worker[kWorkerStatus] === 'started'
|
|
1045
1096
|
worker[kWorkerStatus] = 'exited'
|
|
1097
|
+
this.emit('service:worker:exited', eventPayload)
|
|
1046
1098
|
|
|
1047
1099
|
this.#cleanupWorker(workerId, worker)
|
|
1048
1100
|
|
|
@@ -1053,17 +1105,24 @@ class Runtime extends EventEmitter {
|
|
|
1053
1105
|
// Wait for the next tick so that crashed from the thread are logged first
|
|
1054
1106
|
setImmediate(() => {
|
|
1055
1107
|
if (started && (!config.watch || code !== 0)) {
|
|
1108
|
+
this.emit('service:worker:error', { ...eventPayload, code })
|
|
1056
1109
|
this.logger.warn(`The ${errorLabel} unexpectedly exited with code ${code}.`)
|
|
1057
1110
|
}
|
|
1058
1111
|
|
|
1059
1112
|
// Restart the service if it was started
|
|
1060
1113
|
if (started && this.#status === 'started') {
|
|
1061
1114
|
if (restartOnError > 0) {
|
|
1062
|
-
|
|
1115
|
+
if (restartOnError < IMMEDIATE_RESTART_MAX_THRESHOLD) {
|
|
1116
|
+
this.logger.warn(`The ${errorLabel} is being restarted ...`)
|
|
1117
|
+
} else {
|
|
1118
|
+
this.logger.warn(`The ${errorLabel} will be restarted in ${restartOnError}ms ...`)
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1063
1121
|
this.#restartCrashedWorker(config, serviceConfig, workersCount, serviceId, index, false, 0).catch(err => {
|
|
1064
1122
|
this.logger.error({ err: ensureLoggableError(err) }, `${errorLabel} could not be restarted.`)
|
|
1065
1123
|
})
|
|
1066
1124
|
} else {
|
|
1125
|
+
this.emit('service:worker:unvailable', eventPayload)
|
|
1067
1126
|
this.logger.warn(`The ${errorLabel} is no longer available.`)
|
|
1068
1127
|
}
|
|
1069
1128
|
}
|
|
@@ -1073,6 +1132,7 @@ class Runtime extends EventEmitter {
|
|
|
1073
1132
|
worker[kId] = workersCount > 1 ? workerId : serviceId
|
|
1074
1133
|
worker[kServiceId] = serviceId
|
|
1075
1134
|
worker[kWorkerId] = workersCount > 1 ? index : undefined
|
|
1135
|
+
worker[kForwardEvents] = false
|
|
1076
1136
|
|
|
1077
1137
|
if (inspectorOptions) {
|
|
1078
1138
|
worker[kInspectorOptions] = {
|
|
@@ -1090,10 +1150,14 @@ class Runtime extends EventEmitter {
|
|
|
1090
1150
|
getServiceMeta: this.getServiceMeta.bind(this),
|
|
1091
1151
|
listServices: () => this.#servicesIds,
|
|
1092
1152
|
getServices: this.getServices.bind(this),
|
|
1153
|
+
getWorkers: this.getWorkers.bind(this),
|
|
1093
1154
|
getHttpCacheValue: this.#getHttpCacheValue.bind(this),
|
|
1094
1155
|
setHttpCacheValue: this.#setHttpCacheValue.bind(this),
|
|
1095
1156
|
deleteHttpCacheValue: this.#deleteHttpCacheValue.bind(this),
|
|
1096
|
-
invalidateHttpCache: this.invalidateHttpCache.bind(this)
|
|
1157
|
+
invalidateHttpCache: this.invalidateHttpCache.bind(this),
|
|
1158
|
+
setEventsForwarding (value) {
|
|
1159
|
+
worker[kForwardEvents] = value
|
|
1160
|
+
}
|
|
1097
1161
|
}
|
|
1098
1162
|
})
|
|
1099
1163
|
worker[kITC].listen()
|
|
@@ -1105,6 +1169,8 @@ class Runtime extends EventEmitter {
|
|
|
1105
1169
|
// so that services can eventually manually trigger a restart. This mechanism is current
|
|
1106
1170
|
// used by the composer.
|
|
1107
1171
|
worker[kITC].on('changed', async () => {
|
|
1172
|
+
this.emit('service:worker:changed', eventPayload)
|
|
1173
|
+
|
|
1108
1174
|
try {
|
|
1109
1175
|
const wasStarted = worker[kWorkerStatus].startsWith('start')
|
|
1110
1176
|
await this.stopService(serviceId)
|
|
@@ -1147,6 +1213,7 @@ class Runtime extends EventEmitter {
|
|
|
1147
1213
|
// This must be done here as the dependencies are filled above
|
|
1148
1214
|
worker[kConfig] = { ...serviceConfig, health }
|
|
1149
1215
|
worker[kWorkerStatus] = 'boot'
|
|
1216
|
+
this.emit('service:worker:boot', eventPayload)
|
|
1150
1217
|
}
|
|
1151
1218
|
|
|
1152
1219
|
#setupHealthCheck (worker, errorLabel) {
|
|
@@ -1158,24 +1225,42 @@ class Runtime extends EventEmitter {
|
|
|
1158
1225
|
|
|
1159
1226
|
worker[kHealthCheckTimer] = setTimeout(async () => {
|
|
1160
1227
|
const health = await worker[kITC].send('getHealth')
|
|
1228
|
+
const { elu, heapUsed } = health
|
|
1229
|
+
const memoryUsage = heapUsed / maxHeapTotal
|
|
1230
|
+
const unhealthy = elu > maxELU || memoryUsage > maxHeapUsed
|
|
1231
|
+
|
|
1161
1232
|
this.emit('health', {
|
|
1162
1233
|
id: worker[kId],
|
|
1163
1234
|
service: worker[kServiceId],
|
|
1164
1235
|
worker: worker[kWorkerId],
|
|
1165
1236
|
currentHealth: health,
|
|
1237
|
+
unhealthy,
|
|
1166
1238
|
healthConfig: worker[kConfig].health
|
|
1167
1239
|
})
|
|
1168
1240
|
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1241
|
+
if (unhealthy) {
|
|
1242
|
+
if (elu > maxELU) {
|
|
1243
|
+
this.logger.error(
|
|
1244
|
+
`The ${errorLabel} has an ELU of ${(elu * 100).toFixed(2)} %, above the maximum allowed usage of ${(maxELU * 100).toFixed(2)} %.`
|
|
1245
|
+
)
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
if (memoryUsage > maxHeapUsed) {
|
|
1249
|
+
this.logger.error(
|
|
1250
|
+
`The ${errorLabel} is using ${(elu * 100).toFixed(2)} % of the memory, above the maximum allowed usage of ${(maxELU * 100).toFixed(2)} %.`
|
|
1251
|
+
)
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1172
1254
|
unhealthyChecks++
|
|
1173
1255
|
} else {
|
|
1174
1256
|
unhealthyChecks = 0
|
|
1175
1257
|
}
|
|
1176
1258
|
|
|
1177
1259
|
if (unhealthyChecks >= maxUnhealthyChecks) {
|
|
1178
|
-
this.logger.error(
|
|
1260
|
+
this.logger.error(
|
|
1261
|
+
{ elu, maxELU, memoryUsage, maxMemoryUsage: maxHeapUsed },
|
|
1262
|
+
`The ${errorLabel} is unhealthy. Forcefully terminating it ...`
|
|
1263
|
+
)
|
|
1179
1264
|
worker.terminate()
|
|
1180
1265
|
return
|
|
1181
1266
|
}
|
|
@@ -1193,6 +1278,7 @@ class Runtime extends EventEmitter {
|
|
|
1193
1278
|
}
|
|
1194
1279
|
|
|
1195
1280
|
let worker = await this.#getWorkerById(id, index, false, false)
|
|
1281
|
+
const eventPayload = { service: id, worker: index, workersCount }
|
|
1196
1282
|
|
|
1197
1283
|
// The service was stopped, recreate the thread
|
|
1198
1284
|
if (!worker) {
|
|
@@ -1201,6 +1287,7 @@ class Runtime extends EventEmitter {
|
|
|
1201
1287
|
}
|
|
1202
1288
|
|
|
1203
1289
|
worker[kWorkerStatus] = 'starting'
|
|
1290
|
+
this.emit('service:worker:starting', eventPayload)
|
|
1204
1291
|
|
|
1205
1292
|
try {
|
|
1206
1293
|
let workerUrl
|
|
@@ -1208,6 +1295,7 @@ class Runtime extends EventEmitter {
|
|
|
1208
1295
|
workerUrl = await executeWithTimeout(sendViaITC(worker, 'start'), config.startTimeout)
|
|
1209
1296
|
|
|
1210
1297
|
if (workerUrl === 'timeout') {
|
|
1298
|
+
this.emit('service:worker:startTimeout', eventPayload)
|
|
1211
1299
|
this.logger.info(`The ${label} failed to start in ${config.startTimeout}ms. Forcefully killing the thread.`)
|
|
1212
1300
|
worker.terminate()
|
|
1213
1301
|
throw new errors.ServiceStartTimeoutError(id, config.startTimeout)
|
|
@@ -1223,6 +1311,7 @@ class Runtime extends EventEmitter {
|
|
|
1223
1311
|
}
|
|
1224
1312
|
|
|
1225
1313
|
worker[kWorkerStatus] = 'started'
|
|
1314
|
+
this.emit('service:worker:started', eventPayload)
|
|
1226
1315
|
|
|
1227
1316
|
if (!silent) {
|
|
1228
1317
|
this.logger?.info(`Started the ${label}...`)
|
|
@@ -1249,6 +1338,8 @@ class Runtime extends EventEmitter {
|
|
|
1249
1338
|
await worker.terminate()
|
|
1250
1339
|
}
|
|
1251
1340
|
|
|
1341
|
+
this.emit('service:worker:start:error', eventPayload)
|
|
1342
|
+
|
|
1252
1343
|
if (error.code !== 'PLT_RUNTIME_SERVICE_START_TIMEOUT') {
|
|
1253
1344
|
this.logger.error({ err: ensureLoggableError(error) }, `Failed to start ${label}.`)
|
|
1254
1345
|
}
|
|
@@ -1264,9 +1355,15 @@ class Runtime extends EventEmitter {
|
|
|
1264
1355
|
throw error
|
|
1265
1356
|
}
|
|
1266
1357
|
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1358
|
+
if (restartOnError < IMMEDIATE_RESTART_MAX_THRESHOLD) {
|
|
1359
|
+
this.logger.warn(
|
|
1360
|
+
`Performing attempt ${bootstrapAttempt} of ${MAX_BOOTSTRAP_ATTEMPTS} to start the ${label} again ...`
|
|
1361
|
+
)
|
|
1362
|
+
} else {
|
|
1363
|
+
this.logger.warn(
|
|
1364
|
+
`Attempt ${bootstrapAttempt} of ${MAX_BOOTSTRAP_ATTEMPTS} to start the ${label} again will be performed in ${restartOnError}ms ...`
|
|
1365
|
+
)
|
|
1366
|
+
}
|
|
1270
1367
|
|
|
1271
1368
|
await this.#restartCrashedWorker(config, serviceConfig, workersCount, id, index, silent, bootstrapAttempt)
|
|
1272
1369
|
}
|
|
@@ -1279,7 +1376,10 @@ class Runtime extends EventEmitter {
|
|
|
1279
1376
|
return
|
|
1280
1377
|
}
|
|
1281
1378
|
|
|
1379
|
+
const eventPayload = { service: id, worker: index, workersCount }
|
|
1380
|
+
|
|
1282
1381
|
worker[kWorkerStatus] = 'stopping'
|
|
1382
|
+
this.emit('service:worker:stopping', eventPayload)
|
|
1283
1383
|
|
|
1284
1384
|
const label = this.#workerExtendedLabel(id, index, workersCount)
|
|
1285
1385
|
|
|
@@ -1294,6 +1394,7 @@ class Runtime extends EventEmitter {
|
|
|
1294
1394
|
try {
|
|
1295
1395
|
await executeWithTimeout(sendViaITC(worker, 'stop'), exitTimeout)
|
|
1296
1396
|
} catch (error) {
|
|
1397
|
+
this.emit('service:worker:stop:timeout', eventPayload)
|
|
1297
1398
|
this.logger?.info({ error: ensureLoggableError(error) }, `Failed to stop ${label}. Killing a worker thread.`)
|
|
1298
1399
|
} finally {
|
|
1299
1400
|
worker[kITC].close()
|
|
@@ -1308,12 +1409,14 @@ class Runtime extends EventEmitter {
|
|
|
1308
1409
|
|
|
1309
1410
|
// If the worker didn't exit in time, kill it
|
|
1310
1411
|
if (res === 'timeout') {
|
|
1412
|
+
this.emit('service:worker:exit:timeout', eventPayload)
|
|
1311
1413
|
await worker.terminate()
|
|
1312
1414
|
}
|
|
1313
1415
|
|
|
1314
1416
|
await this.#avoidOutOfOrderThreadLogs()
|
|
1315
1417
|
|
|
1316
1418
|
worker[kWorkerStatus] = 'stopped'
|
|
1419
|
+
this.emit('service:worker:stopped', eventPayload)
|
|
1317
1420
|
}
|
|
1318
1421
|
|
|
1319
1422
|
#cleanupWorker (workerId, worker) {
|
|
@@ -1338,7 +1441,7 @@ class Runtime extends EventEmitter {
|
|
|
1338
1441
|
}
|
|
1339
1442
|
|
|
1340
1443
|
restartPromise = new Promise((resolve, reject) => {
|
|
1341
|
-
|
|
1444
|
+
async function restart () {
|
|
1342
1445
|
this.#restartingWorkers.delete(workerId)
|
|
1343
1446
|
|
|
1344
1447
|
// If some processes were scheduled to restart
|
|
@@ -1360,7 +1463,13 @@ class Runtime extends EventEmitter {
|
|
|
1360
1463
|
|
|
1361
1464
|
reject(err)
|
|
1362
1465
|
}
|
|
1363
|
-
}
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
if (config.restartOnError < IMMEDIATE_RESTART_MAX_THRESHOLD) {
|
|
1469
|
+
process.nextTick(restart.bind(this))
|
|
1470
|
+
} else {
|
|
1471
|
+
setTimeout(restart.bind(this), config.restartOnError)
|
|
1472
|
+
}
|
|
1364
1473
|
})
|
|
1365
1474
|
|
|
1366
1475
|
this.#restartingWorkers.set(workerId, restartPromise)
|
package/lib/schema.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/runtime",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.44.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -36,12 +36,12 @@
|
|
|
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": "2.
|
|
40
|
-
"@platformatic/db": "2.
|
|
41
|
-
"@platformatic/
|
|
42
|
-
"@platformatic/
|
|
43
|
-
"@platformatic/sql-
|
|
44
|
-
"@platformatic/sql-
|
|
39
|
+
"@platformatic/composer": "2.44.0",
|
|
40
|
+
"@platformatic/db": "2.44.0",
|
|
41
|
+
"@platformatic/node": "2.44.0",
|
|
42
|
+
"@platformatic/service": "2.44.0",
|
|
43
|
+
"@platformatic/sql-graphql": "2.44.0",
|
|
44
|
+
"@platformatic/sql-mapper": "2.44.0"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@fastify/accepts": "^5.0.0",
|
|
@@ -75,13 +75,13 @@
|
|
|
75
75
|
"undici": "^7.0.0",
|
|
76
76
|
"undici-thread-interceptor": "^0.11.0",
|
|
77
77
|
"ws": "^8.16.0",
|
|
78
|
-
"@platformatic/basic": "2.
|
|
79
|
-
"@platformatic/
|
|
80
|
-
"@platformatic/
|
|
81
|
-
"@platformatic/itc": "2.
|
|
82
|
-
"@platformatic/telemetry": "2.
|
|
83
|
-
"@platformatic/
|
|
84
|
-
"@platformatic/
|
|
78
|
+
"@platformatic/basic": "2.44.0",
|
|
79
|
+
"@platformatic/config": "2.44.0",
|
|
80
|
+
"@platformatic/generators": "2.44.0",
|
|
81
|
+
"@platformatic/itc": "2.44.0",
|
|
82
|
+
"@platformatic/telemetry": "2.44.0",
|
|
83
|
+
"@platformatic/utils": "2.44.0",
|
|
84
|
+
"@platformatic/ts-compiler": "2.44.0"
|
|
85
85
|
},
|
|
86
86
|
"scripts": {
|
|
87
87
|
"test": "npm run lint && borp --concurrency=1 --timeout=300000 && tsd",
|
package/schema.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://schemas.platformatic.dev/@platformatic/runtime/2.
|
|
2
|
+
"$id": "https://schemas.platformatic.dev/@platformatic/runtime/2.44.0.json",
|
|
3
3
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
]
|
|
114
114
|
},
|
|
115
115
|
"maxUnhealthyChecks": {
|
|
116
|
-
"default":
|
|
116
|
+
"default": 10,
|
|
117
117
|
"anyOf": [
|
|
118
118
|
{
|
|
119
119
|
"type": "number",
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
]
|
|
126
126
|
},
|
|
127
127
|
"maxELU": {
|
|
128
|
-
"default": 0.
|
|
128
|
+
"default": 0.99,
|
|
129
129
|
"anyOf": [
|
|
130
130
|
{
|
|
131
131
|
"type": "number",
|
|
@@ -138,7 +138,7 @@
|
|
|
138
138
|
]
|
|
139
139
|
},
|
|
140
140
|
"maxHeapUsed": {
|
|
141
|
-
"default": 0.
|
|
141
|
+
"default": 0.99,
|
|
142
142
|
"anyOf": [
|
|
143
143
|
{
|
|
144
144
|
"type": "number",
|
|
@@ -284,7 +284,7 @@
|
|
|
284
284
|
]
|
|
285
285
|
},
|
|
286
286
|
"maxUnhealthyChecks": {
|
|
287
|
-
"default":
|
|
287
|
+
"default": 10,
|
|
288
288
|
"anyOf": [
|
|
289
289
|
{
|
|
290
290
|
"type": "number",
|
|
@@ -296,7 +296,7 @@
|
|
|
296
296
|
]
|
|
297
297
|
},
|
|
298
298
|
"maxELU": {
|
|
299
|
-
"default": 0.
|
|
299
|
+
"default": 0.99,
|
|
300
300
|
"anyOf": [
|
|
301
301
|
{
|
|
302
302
|
"type": "number",
|
|
@@ -309,7 +309,7 @@
|
|
|
309
309
|
]
|
|
310
310
|
},
|
|
311
311
|
"maxHeapUsed": {
|
|
312
|
-
"default": 0.
|
|
312
|
+
"default": 0.99,
|
|
313
313
|
"anyOf": [
|
|
314
314
|
{
|
|
315
315
|
"type": "number",
|
|
@@ -520,7 +520,7 @@
|
|
|
520
520
|
]
|
|
521
521
|
},
|
|
522
522
|
"maxUnhealthyChecks": {
|
|
523
|
-
"default":
|
|
523
|
+
"default": 10,
|
|
524
524
|
"anyOf": [
|
|
525
525
|
{
|
|
526
526
|
"type": "number",
|
|
@@ -532,7 +532,7 @@
|
|
|
532
532
|
]
|
|
533
533
|
},
|
|
534
534
|
"maxELU": {
|
|
535
|
-
"default": 0.
|
|
535
|
+
"default": 0.99,
|
|
536
536
|
"anyOf": [
|
|
537
537
|
{
|
|
538
538
|
"type": "number",
|
|
@@ -545,7 +545,7 @@
|
|
|
545
545
|
]
|
|
546
546
|
},
|
|
547
547
|
"maxHeapUsed": {
|
|
548
|
-
"default": 0.
|
|
548
|
+
"default": 0.99,
|
|
549
549
|
"anyOf": [
|
|
550
550
|
{
|
|
551
551
|
"type": "number",
|
|
@@ -870,7 +870,7 @@
|
|
|
870
870
|
},
|
|
871
871
|
{
|
|
872
872
|
"type": "number",
|
|
873
|
-
"minimum":
|
|
873
|
+
"minimum": 0
|
|
874
874
|
}
|
|
875
875
|
]
|
|
876
876
|
},
|
|
@@ -949,7 +949,7 @@
|
|
|
949
949
|
]
|
|
950
950
|
},
|
|
951
951
|
"maxUnhealthyChecks": {
|
|
952
|
-
"default":
|
|
952
|
+
"default": 10,
|
|
953
953
|
"anyOf": [
|
|
954
954
|
{
|
|
955
955
|
"type": "number",
|
|
@@ -961,7 +961,7 @@
|
|
|
961
961
|
]
|
|
962
962
|
},
|
|
963
963
|
"maxELU": {
|
|
964
|
-
"default": 0.
|
|
964
|
+
"default": 0.99,
|
|
965
965
|
"anyOf": [
|
|
966
966
|
{
|
|
967
967
|
"type": "number",
|
|
@@ -974,7 +974,7 @@
|
|
|
974
974
|
]
|
|
975
975
|
},
|
|
976
976
|
"maxHeapUsed": {
|
|
977
|
-
"default": 0.
|
|
977
|
+
"default": 0.99,
|
|
978
978
|
"anyOf": [
|
|
979
979
|
{
|
|
980
980
|
"type": "number",
|