@platformatic/runtime 2.69.0 → 2.70.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 +3 -3
- package/lib/errors.js +1 -0
- package/lib/runtime.js +328 -42
- package/lib/worker/interceptors.js +4 -2
- package/lib/worker/symbols.js +2 -0
- package/package.json +18 -18
- package/schema.json +54 -23
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 HttpsSchemasPlatformaticDevPlatformaticRuntime2700Json = {
|
|
9
9
|
[k: string]: unknown;
|
|
10
10
|
} & {
|
|
11
11
|
$schema?: string;
|
|
@@ -29,7 +29,7 @@ export type HttpsSchemasPlatformaticDevPlatformaticRuntime2690Json = {
|
|
|
29
29
|
maxELU?: number | string;
|
|
30
30
|
maxHeapUsed?: number | string;
|
|
31
31
|
maxHeapTotal?: number | string;
|
|
32
|
-
maxYoungGeneration?: number;
|
|
32
|
+
maxYoungGeneration?: number | string;
|
|
33
33
|
};
|
|
34
34
|
preload?: string | string[];
|
|
35
35
|
arguments?: string[];
|
|
@@ -141,7 +141,7 @@ export type HttpsSchemasPlatformaticDevPlatformaticRuntime2690Json = {
|
|
|
141
141
|
maxELU?: number | string;
|
|
142
142
|
maxHeapUsed?: number | string;
|
|
143
143
|
maxHeapTotal?: number | string;
|
|
144
|
-
maxYoungGeneration?: number;
|
|
144
|
+
maxYoungGeneration?: number | string;
|
|
145
145
|
};
|
|
146
146
|
undici?: {
|
|
147
147
|
agentOptions?: {
|
package/lib/errors.js
CHANGED
|
@@ -13,6 +13,7 @@ module.exports = {
|
|
|
13
13
|
WorkerExitedError: createError(`${ERROR_PREFIX}_SERVICE_EXIT`, 'The worker %s of the service "%s" exited prematurely with error code %d'),
|
|
14
14
|
UnknownRuntimeAPICommandError: createError(`${ERROR_PREFIX}_UNKNOWN_RUNTIME_API_COMMAND`, 'Unknown Runtime API command "%s"'),
|
|
15
15
|
ServiceNotFoundError: createError(`${ERROR_PREFIX}_SERVICE_NOT_FOUND`, 'Service %s not found. Available services are: %s'),
|
|
16
|
+
ServiceWorkerNotFoundError: createError(`${ERROR_PREFIX}_SERVICE_WORKER_NOT_FOUND`, 'Service %s worker %s not found'),
|
|
16
17
|
ServiceNotStartedError: createError(`${ERROR_PREFIX}_SERVICE_NOT_STARTED`, "Service with id '%s' is not started"),
|
|
17
18
|
ServiceStartTimeoutError: createError(`${ERROR_PREFIX}_SERVICE_START_TIMEOUT`, "Service with id '%s' failed to start in %dms."),
|
|
18
19
|
FailedToRetrieveOpenAPISchemaError: createError(`${ERROR_PREFIX}_FAILED_TO_RETRIEVE_OPENAPI_SCHEMA`, 'Failed to retrieve OpenAPI schema for service with id "%s": %s'),
|
package/lib/runtime.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { ITC } = require('@platformatic/itc')
|
|
4
|
-
const { features, ensureLoggableError, executeWithTimeout, deepmerge } = require('@platformatic/utils')
|
|
4
|
+
const { features, ensureLoggableError, executeWithTimeout, deepmerge, parseMemorySize } = require('@platformatic/utils')
|
|
5
5
|
const { once, EventEmitter } = require('node:events')
|
|
6
6
|
const { createReadStream, watch, existsSync } = require('node:fs')
|
|
7
7
|
const { readdir, readFile, stat, access } = require('node:fs/promises')
|
|
@@ -33,7 +33,8 @@ const {
|
|
|
33
33
|
kHealthCheckTimer,
|
|
34
34
|
kConfig,
|
|
35
35
|
kWorkerStatus,
|
|
36
|
-
kStderrMarker
|
|
36
|
+
kStderrMarker,
|
|
37
|
+
kLastELU
|
|
37
38
|
} = require('./worker/symbols')
|
|
38
39
|
|
|
39
40
|
const fastify = require('fastify')
|
|
@@ -1148,10 +1149,13 @@ class Runtime extends EventEmitter {
|
|
|
1148
1149
|
workerEnv['NODE_OPTIONS'] = `${originalNodeOptions} ${serviceConfig.nodeOptions}`.trim()
|
|
1149
1150
|
}
|
|
1150
1151
|
|
|
1152
|
+
const maxHeapTotal = typeof health.maxHeapTotal === 'string' ? parseMemorySize(health.maxHeapTotal) : health.maxHeapTotal
|
|
1153
|
+
const maxYoungGeneration = typeof health.maxYoungGeneration === 'string' ? parseMemorySize(health.maxYoungGeneration) : health.maxYoungGeneration
|
|
1154
|
+
|
|
1151
1155
|
const maxOldGenerationSizeMb = Math.floor(
|
|
1152
|
-
(
|
|
1156
|
+
(maxYoungGeneration > 0 ? maxHeapTotal - maxYoungGeneration : maxHeapTotal) / (1024 * 1024)
|
|
1153
1157
|
)
|
|
1154
|
-
const maxYoungGenerationSizeMb =
|
|
1158
|
+
const maxYoungGenerationSizeMb = maxYoungGeneration ? Math.floor(maxYoungGeneration / (1024 * 1024)) : undefined
|
|
1155
1159
|
|
|
1156
1160
|
const worker = new Worker(kWorkerFile, {
|
|
1157
1161
|
workerData: {
|
|
@@ -1323,18 +1327,39 @@ class Runtime extends EventEmitter {
|
|
|
1323
1327
|
return worker
|
|
1324
1328
|
}
|
|
1325
1329
|
|
|
1326
|
-
#
|
|
1330
|
+
async #getHealth (worker) {
|
|
1331
|
+
if (features.node.worker.getHeapStatistics) {
|
|
1332
|
+
const { used_heap_size: heapUsed, total_heap_size: heapTotal } = await worker.getHeapStatistics()
|
|
1333
|
+
const currentELU = worker.performance.eventLoopUtilization()
|
|
1334
|
+
const elu = worker.performance.eventLoopUtilization(currentELU, worker[kLastELU]).utilization
|
|
1335
|
+
worker[kLastELU] = currentELU
|
|
1336
|
+
return { elu, heapUsed, heapTotal }
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
const health = await worker[kITC].send('getHealth')
|
|
1340
|
+
return health
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
#setupHealthCheck (config, serviceConfig, workersCount, id, index, worker, errorLabel) {
|
|
1327
1344
|
// Clear the timeout when exiting
|
|
1328
1345
|
worker.on('exit', () => clearTimeout(worker[kHealthCheckTimer]))
|
|
1329
1346
|
|
|
1330
1347
|
const { maxELU, maxHeapUsed, maxHeapTotal, maxUnhealthyChecks, interval } = worker[kConfig].health
|
|
1348
|
+
const maxHeapTotalNumber = typeof maxHeapTotal === 'string' ? parseMemorySize(maxHeapTotal) : maxHeapTotal
|
|
1349
|
+
|
|
1331
1350
|
let unhealthyChecks = 0
|
|
1332
1351
|
|
|
1333
1352
|
worker[kHealthCheckTimer] = setTimeout(async () => {
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1353
|
+
let health, unhealthy, memoryUsage
|
|
1354
|
+
try {
|
|
1355
|
+
health = await this.#getHealth(worker)
|
|
1356
|
+
memoryUsage = health.heapUsed / maxHeapTotalNumber
|
|
1357
|
+
unhealthy = health.elu > maxELU || memoryUsage > maxHeapUsed
|
|
1358
|
+
} catch (err) {
|
|
1359
|
+
this.logger.error({ err }, `Failed to get health for ${errorLabel}.`)
|
|
1360
|
+
unhealthy = true
|
|
1361
|
+
memoryUsage = 0
|
|
1362
|
+
}
|
|
1338
1363
|
|
|
1339
1364
|
const serviceId = worker[kServiceId]
|
|
1340
1365
|
this.emit('health', {
|
|
@@ -1347,9 +1372,9 @@ class Runtime extends EventEmitter {
|
|
|
1347
1372
|
})
|
|
1348
1373
|
|
|
1349
1374
|
if (unhealthy) {
|
|
1350
|
-
if (elu > maxELU) {
|
|
1375
|
+
if (health.elu > maxELU) {
|
|
1351
1376
|
this.logger.error(
|
|
1352
|
-
`The ${errorLabel} has an ELU of ${(elu * 100).toFixed(2)} %, above the maximum allowed usage of ${(maxELU * 100).toFixed(2)} %.`
|
|
1377
|
+
`The ${errorLabel} has an ELU of ${(health.elu * 100).toFixed(2)} %, above the maximum allowed usage of ${(maxELU * 100).toFixed(2)} %.`
|
|
1353
1378
|
)
|
|
1354
1379
|
}
|
|
1355
1380
|
|
|
@@ -1367,14 +1392,14 @@ class Runtime extends EventEmitter {
|
|
|
1367
1392
|
if (unhealthyChecks === maxUnhealthyChecks) {
|
|
1368
1393
|
try {
|
|
1369
1394
|
this.logger.error(
|
|
1370
|
-
{ elu, maxELU, memoryUsage, maxMemoryUsage: maxHeapUsed },
|
|
1395
|
+
{ elu: health.elu, maxELU, memoryUsage: health.heapUsed, maxMemoryUsage: maxHeapUsed },
|
|
1371
1396
|
`The ${errorLabel} is unhealthy. Replacing it ...`
|
|
1372
1397
|
)
|
|
1373
1398
|
|
|
1374
|
-
await this.#replaceWorker(config, serviceConfig, workersCount, id, index,
|
|
1399
|
+
await this.#replaceWorker(config, serviceConfig, workersCount, id, index, worker)
|
|
1375
1400
|
} catch (e) {
|
|
1376
1401
|
this.logger.error(
|
|
1377
|
-
{ elu, maxELU, memoryUsage, maxMemoryUsage: maxHeapUsed },
|
|
1402
|
+
{ elu: health.elu, maxELU, memoryUsage: health.heapUsed, maxMemoryUsage: maxHeapUsed },
|
|
1378
1403
|
`Cannot replace the ${errorLabel}. Forcefully terminating it ...`
|
|
1379
1404
|
)
|
|
1380
1405
|
|
|
@@ -1397,7 +1422,6 @@ class Runtime extends EventEmitter {
|
|
|
1397
1422
|
worker = undefined,
|
|
1398
1423
|
disableRestartAttempts = false
|
|
1399
1424
|
) {
|
|
1400
|
-
const workerId = `${id}:${index}`
|
|
1401
1425
|
const label = this.#workerExtendedLabel(id, index, workersCount)
|
|
1402
1426
|
|
|
1403
1427
|
if (!silent) {
|
|
@@ -1451,7 +1475,7 @@ class Runtime extends EventEmitter {
|
|
|
1451
1475
|
if (enabled && config.restartOnError > 0) {
|
|
1452
1476
|
worker[kHealthCheckTimer] = setTimeout(
|
|
1453
1477
|
() => {
|
|
1454
|
-
this.#setupHealthCheck(config, serviceConfig, workersCount, id, index,
|
|
1478
|
+
this.#setupHealthCheck(config, serviceConfig, workersCount, id, index, worker, label)
|
|
1455
1479
|
},
|
|
1456
1480
|
gracePeriod > 0 ? gracePeriod : 1
|
|
1457
1481
|
)
|
|
@@ -1625,7 +1649,8 @@ class Runtime extends EventEmitter {
|
|
|
1625
1649
|
await restartPromise
|
|
1626
1650
|
}
|
|
1627
1651
|
|
|
1628
|
-
async #replaceWorker (config, serviceConfig, workersCount, serviceId, index,
|
|
1652
|
+
async #replaceWorker (config, serviceConfig, workersCount, serviceId, index, worker) {
|
|
1653
|
+
const workerId = `${serviceId}:${index}`
|
|
1629
1654
|
let newWorker
|
|
1630
1655
|
|
|
1631
1656
|
try {
|
|
@@ -1685,7 +1710,7 @@ class Runtime extends EventEmitter {
|
|
|
1685
1710
|
return null
|
|
1686
1711
|
}
|
|
1687
1712
|
|
|
1688
|
-
throw new errors.
|
|
1713
|
+
throw new errors.ServiceWorkerNotFoundError(serviceId, workerId)
|
|
1689
1714
|
}
|
|
1690
1715
|
|
|
1691
1716
|
if (ensureStarted) {
|
|
@@ -1849,10 +1874,16 @@ class Runtime extends EventEmitter {
|
|
|
1849
1874
|
|
|
1850
1875
|
async getServiceResourcesInfo (id) {
|
|
1851
1876
|
const workers = this.#workers.getCount(id)
|
|
1852
|
-
|
|
1877
|
+
|
|
1878
|
+
const worker = await this.#getWorkerById(id, 0, false, false)
|
|
1879
|
+
const health = worker[kConfig].health
|
|
1880
|
+
|
|
1881
|
+
return { workers, health }
|
|
1853
1882
|
}
|
|
1854
1883
|
|
|
1855
|
-
async #
|
|
1884
|
+
async #updateServiceConfigWorkers (serviceId, workers) {
|
|
1885
|
+
this.logger.info(`Updating service "${serviceId}" config workers to ${workers}`)
|
|
1886
|
+
|
|
1856
1887
|
this.#configManager.current.services.find(s => s.id === serviceId).workers = workers
|
|
1857
1888
|
const service = await this.#getServiceById(serviceId)
|
|
1858
1889
|
this.#workers.setCount(serviceId, workers)
|
|
@@ -1868,14 +1899,109 @@ class Runtime extends EventEmitter {
|
|
|
1868
1899
|
const results = await Promise.allSettled(promises)
|
|
1869
1900
|
for (const result of results) {
|
|
1870
1901
|
if (result.status === 'rejected') {
|
|
1902
|
+
this.logger.error({ err: result.reason }, `Cannot update service "${serviceId}" workers`)
|
|
1871
1903
|
throw result.reason
|
|
1872
1904
|
}
|
|
1873
1905
|
}
|
|
1874
1906
|
}
|
|
1875
1907
|
|
|
1908
|
+
async #updateServiceConfigHealth (serviceId, health) {
|
|
1909
|
+
this.logger.info(`Updating service "${serviceId}" config health heap to ${JSON.stringify(health)}`)
|
|
1910
|
+
const { maxHeapTotal, maxYoungGeneration } = health
|
|
1911
|
+
|
|
1912
|
+
const service = this.#configManager.current.services.find(s => s.id === serviceId)
|
|
1913
|
+
if (maxHeapTotal) {
|
|
1914
|
+
service.health.maxHeapTotal = maxHeapTotal
|
|
1915
|
+
}
|
|
1916
|
+
if (maxYoungGeneration) {
|
|
1917
|
+
service.health.maxYoungGeneration = maxYoungGeneration
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
/**
|
|
1922
|
+
* Updates the resources of the services, such as the number of workers and health configurations (e.g., heap memory settings).
|
|
1923
|
+
*
|
|
1924
|
+
* This function handles three update scenarios for each service:
|
|
1925
|
+
* 1. **Updating workers only**: Adjusts the number of workers for the service.
|
|
1926
|
+
* 2. **Updating health configurations only**: Updates health parameters like `maxHeapTotal` or `maxYoungGeneration`.
|
|
1927
|
+
* 3. **Updating both workers and health configurations**: Scales the workers and also applies health settings.
|
|
1928
|
+
*
|
|
1929
|
+
* When updating both workers and health:
|
|
1930
|
+
* - **Scaling down workers**: Stops extra workers, then restarts the remaining workers with the previous settings.
|
|
1931
|
+
* - **Scaling up workers**: Starts new workers with the updated heap settings, then restarts the old workers with the updated settings.
|
|
1932
|
+
*
|
|
1933
|
+
* Scaling up new resources (workers and/or heap memory) may fails due to insufficient memory, in this case the operation may fail partially or entirely.
|
|
1934
|
+
* Scaling down is expected to succeed without issues.
|
|
1935
|
+
*
|
|
1936
|
+
* @param {Array<Object>} updates - An array of objects that define the updates for each service.
|
|
1937
|
+
* @param {string} updates[].service - The ID of the service to update.
|
|
1938
|
+
* @param {number} [updates[].workers] - The desired number of workers for the service. If omitted, workers will not be updated.
|
|
1939
|
+
* @param {Object} [updates[].health] - The health configuration to update for the service, which may include:
|
|
1940
|
+
* @param {string|number} [updates[].health.maxHeapTotal] - The maximum heap memory for the service. Can be a valid memory string (e.g., '1G', '512MB') or a number representing bytes.
|
|
1941
|
+
* @param {string|number} [updates[].health.maxYoungGeneration] - The maximum young generation memory for the service. Can be a valid memory string (e.g., '128MB') or a number representing bytes.
|
|
1942
|
+
*
|
|
1943
|
+
* @returns {Promise<Array<Object>>} - A promise that resolves to an array of reports for each service, detailing the success or failure of the operations:
|
|
1944
|
+
* - `service`: The service ID.
|
|
1945
|
+
* - `workers`: The workers' update report, including the current, new number of workers, started workers, and success status.
|
|
1946
|
+
* - `health`: The health update report, showing the current and new heap settings, updated workers, and success status.
|
|
1947
|
+
*
|
|
1948
|
+
* @example
|
|
1949
|
+
* await runtime.updateServicesResources([
|
|
1950
|
+
* { service: 'service-1', workers: 2, health: { maxHeapTotal: '1G', maxYoungGeneration: '128 MB' } },
|
|
1951
|
+
* { service: 'service-2', health: { maxHeapTotal: '1G' } },
|
|
1952
|
+
* { service: 'service-3', workers: 2 },
|
|
1953
|
+
* ])
|
|
1954
|
+
*
|
|
1955
|
+
* In this example:
|
|
1956
|
+
* - `service-1` will have 2 workers and updated heap memory configurations.
|
|
1957
|
+
* - `service-2` will have updated heap memory settings (without changing workers).
|
|
1958
|
+
* - `service-3` will have its workers set to 2 but no change in memory settings.
|
|
1959
|
+
*
|
|
1960
|
+
* @throws {InvalidArgumentError} - Throws if any update parameter is invalid, such as:
|
|
1961
|
+
* - Missing service ID.
|
|
1962
|
+
* - Invalid worker count (not a positive integer).
|
|
1963
|
+
* - Invalid memory size format for `maxHeapTotal` or `maxYoungGeneration`.
|
|
1964
|
+
* @throws {ServiceNotFoundError} - Throws if the specified service ID does not exist in the current service configuration.
|
|
1965
|
+
*/
|
|
1876
1966
|
async updateServicesResources (updates) {
|
|
1877
|
-
if (this.#status === 'stopping' || this.#status === 'closed')
|
|
1967
|
+
if (this.#status === 'stopping' || this.#status === 'closed') {
|
|
1968
|
+
this.logger.warn('Cannot update service resources when the runtime is stopping or closed')
|
|
1969
|
+
return
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
const ups = await this.#validateUpdateServiceResources(updates)
|
|
1973
|
+
const config = this.#configManager.current
|
|
1878
1974
|
|
|
1975
|
+
const report = []
|
|
1976
|
+
for (const update of ups) {
|
|
1977
|
+
const { serviceId, config: serviceConfig, workers, health, currentWorkers, currentHealth } = update
|
|
1978
|
+
|
|
1979
|
+
if (workers && health) {
|
|
1980
|
+
const r = await this.#updateServiceWorkersAndHealth(serviceId, config, serviceConfig, workers, health, currentWorkers, currentHealth)
|
|
1981
|
+
report.push({
|
|
1982
|
+
service: serviceId,
|
|
1983
|
+
workers: r.workers,
|
|
1984
|
+
health: r.health
|
|
1985
|
+
})
|
|
1986
|
+
} else if (health) {
|
|
1987
|
+
const r = await this.#updateServiceHealth(serviceId, config, serviceConfig, currentWorkers, currentHealth, health)
|
|
1988
|
+
report.push({
|
|
1989
|
+
service: serviceId,
|
|
1990
|
+
health: r.health
|
|
1991
|
+
})
|
|
1992
|
+
} else if (workers) {
|
|
1993
|
+
const r = await this.#updateServiceWorkers(serviceId, config, serviceConfig, workers, currentWorkers)
|
|
1994
|
+
report.push({
|
|
1995
|
+
service: serviceId,
|
|
1996
|
+
workers: r.workers
|
|
1997
|
+
})
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
return report
|
|
2002
|
+
}
|
|
2003
|
+
|
|
2004
|
+
async #validateUpdateServiceResources (updates) {
|
|
1879
2005
|
if (!Array.isArray(updates)) {
|
|
1880
2006
|
throw new errors.InvalidArgumentError('updates', 'must be an array')
|
|
1881
2007
|
}
|
|
@@ -1884,47 +2010,207 @@ class Runtime extends EventEmitter {
|
|
|
1884
2010
|
}
|
|
1885
2011
|
|
|
1886
2012
|
const config = this.#configManager.current
|
|
1887
|
-
|
|
2013
|
+
const validatedUpdates = []
|
|
1888
2014
|
for (const update of updates) {
|
|
1889
|
-
const { service: serviceId
|
|
2015
|
+
const { service: serviceId } = update
|
|
1890
2016
|
|
|
1891
|
-
if (typeof workers !== 'number') {
|
|
1892
|
-
throw new errors.InvalidArgumentError('workers', 'must be a number')
|
|
1893
|
-
}
|
|
1894
2017
|
if (!serviceId) {
|
|
1895
2018
|
throw new errors.InvalidArgumentError('service', 'must be a string')
|
|
1896
2019
|
}
|
|
1897
|
-
if (workers <= 0) {
|
|
1898
|
-
throw new errors.InvalidArgumentError('workers', 'must be greater than 0')
|
|
1899
|
-
}
|
|
1900
|
-
if (workers > MAX_WORKERS) {
|
|
1901
|
-
throw new errors.InvalidArgumentError('workers', `must be less than ${MAX_WORKERS}`)
|
|
1902
|
-
}
|
|
1903
2020
|
const serviceConfig = config.services.find(s => s.id === serviceId)
|
|
1904
2021
|
if (!serviceConfig) {
|
|
1905
2022
|
throw new errors.ServiceNotFoundError(serviceId, Array.from(this.#servicesIds).join(', '))
|
|
1906
2023
|
}
|
|
1907
2024
|
|
|
1908
|
-
const { workers: currentWorkers } = await this.getServiceResourcesInfo(serviceId)
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
2025
|
+
const { workers: currentWorkers, health: currentHealth } = await this.getServiceResourcesInfo(serviceId)
|
|
2026
|
+
|
|
2027
|
+
let workers
|
|
2028
|
+
if (update.workers !== undefined) {
|
|
2029
|
+
if (typeof update.workers !== 'number') {
|
|
2030
|
+
throw new errors.InvalidArgumentError('workers', 'must be a number')
|
|
2031
|
+
}
|
|
2032
|
+
if (update.workers <= 0) {
|
|
2033
|
+
throw new errors.InvalidArgumentError('workers', 'must be greater than 0')
|
|
2034
|
+
}
|
|
2035
|
+
if (update.workers > MAX_WORKERS) {
|
|
2036
|
+
throw new errors.InvalidArgumentError('workers', `must be less than ${MAX_WORKERS}`)
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
if (currentWorkers === update.workers) {
|
|
2040
|
+
this.logger.warn({ serviceId, workers: update.workers }, 'No change in the number of workers for service')
|
|
2041
|
+
} else {
|
|
2042
|
+
workers = update.workers
|
|
2043
|
+
}
|
|
1912
2044
|
}
|
|
1913
2045
|
|
|
1914
|
-
|
|
1915
|
-
|
|
2046
|
+
let maxHeapTotal, maxYoungGeneration
|
|
2047
|
+
if (update.health) {
|
|
2048
|
+
if (update.health.maxHeapTotal !== undefined) {
|
|
2049
|
+
if (typeof update.health.maxHeapTotal === 'string') {
|
|
2050
|
+
try {
|
|
2051
|
+
maxHeapTotal = parseMemorySize(update.health.maxHeapTotal)
|
|
2052
|
+
} catch {
|
|
2053
|
+
throw new errors.InvalidArgumentError('maxHeapTotal', 'must be a valid memory size')
|
|
2054
|
+
}
|
|
2055
|
+
} else if (typeof update.health.maxHeapTotal === 'number') {
|
|
2056
|
+
maxHeapTotal = update.health.maxHeapTotal
|
|
2057
|
+
if (update.health.maxHeapTotal <= 0) {
|
|
2058
|
+
throw new errors.InvalidArgumentError('maxHeapTotal', 'must be greater than 0')
|
|
2059
|
+
}
|
|
2060
|
+
} else {
|
|
2061
|
+
throw new errors.InvalidArgumentError('maxHeapTotal', 'must be a number or a string representing a memory size')
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
if (currentHealth.maxHeapTotal === maxHeapTotal) {
|
|
2065
|
+
this.logger.warn({ serviceId, maxHeapTotal }, 'No change in the max heap total for service')
|
|
2066
|
+
maxHeapTotal = undefined
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
if (update.health.maxYoungGeneration !== undefined) {
|
|
2071
|
+
if (typeof update.health.maxYoungGeneration === 'string') {
|
|
2072
|
+
try {
|
|
2073
|
+
maxYoungGeneration = parseMemorySize(update.health.maxYoungGeneration)
|
|
2074
|
+
} catch {
|
|
2075
|
+
throw new errors.InvalidArgumentError('maxYoungGeneration', 'must be a valid memory size')
|
|
2076
|
+
}
|
|
2077
|
+
} else if (typeof update.health.maxYoungGeneration === 'number') {
|
|
2078
|
+
maxYoungGeneration = update.health.maxYoungGeneration
|
|
2079
|
+
if (update.health.maxYoungGeneration <= 0) {
|
|
2080
|
+
throw new errors.InvalidArgumentError('maxYoungGeneration', 'must be greater than 0')
|
|
2081
|
+
}
|
|
2082
|
+
} else {
|
|
2083
|
+
throw new errors.InvalidArgumentError('maxYoungGeneration', 'must be a number or a string representing a memory size')
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2086
|
+
if (currentHealth.maxYoungGeneration && currentHealth.maxYoungGeneration === maxYoungGeneration) {
|
|
2087
|
+
this.logger.warn({ serviceId, maxYoungGeneration }, 'No change in the max young generation for service')
|
|
2088
|
+
maxYoungGeneration = undefined
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2093
|
+
if (workers || maxHeapTotal || maxYoungGeneration) {
|
|
2094
|
+
let health
|
|
2095
|
+
if (maxHeapTotal || maxYoungGeneration) {
|
|
2096
|
+
health = { }
|
|
2097
|
+
if (maxHeapTotal) {
|
|
2098
|
+
health.maxHeapTotal = maxHeapTotal
|
|
2099
|
+
}
|
|
2100
|
+
if (maxYoungGeneration) {
|
|
2101
|
+
health.maxYoungGeneration = maxYoungGeneration
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
validatedUpdates.push({ serviceId, config: serviceConfig, workers, health, currentWorkers, currentHealth })
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
return validatedUpdates
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2111
|
+
async #updateServiceWorkersAndHealth (serviceId, config, serviceConfig, workers, health, currentWorkers, currentHealth) {
|
|
2112
|
+
if (currentWorkers > workers) {
|
|
2113
|
+
// stop workers
|
|
2114
|
+
const reportWorkers = await this.#updateServiceWorkers(serviceId, config, serviceConfig, workers, currentWorkers)
|
|
2115
|
+
// update heap for current workers
|
|
2116
|
+
const reportHealth = await this.#updateServiceHealth(serviceId, config, serviceConfig, workers, currentHealth, health)
|
|
2117
|
+
|
|
2118
|
+
return { workers: reportWorkers, health: reportHealth }
|
|
2119
|
+
} else {
|
|
2120
|
+
// update service heap
|
|
2121
|
+
await this.#updateServiceConfigHealth(serviceId, health)
|
|
2122
|
+
// start new workers with new heap
|
|
2123
|
+
const reportWorkers = await this.#updateServiceWorkers(serviceId, config, serviceConfig, workers, currentWorkers)
|
|
2124
|
+
// update heap for current workers
|
|
2125
|
+
const reportHealth = await this.#updateServiceHealth(serviceId, config, serviceConfig, currentWorkers, currentHealth, health, false)
|
|
2126
|
+
|
|
2127
|
+
return { workers: reportWorkers, health: reportHealth }
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
async #updateServiceHealth (serviceId, config, serviceConfig, currentWorkers, currentHealth, health, updateConfig = true) {
|
|
2132
|
+
const report = {
|
|
2133
|
+
current: currentHealth,
|
|
2134
|
+
new: health,
|
|
2135
|
+
updated: []
|
|
2136
|
+
}
|
|
2137
|
+
try {
|
|
2138
|
+
if (updateConfig) {
|
|
2139
|
+
await this.#updateServiceConfigHealth(serviceId, health)
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2142
|
+
for (let i = 0; i < currentWorkers; i++) {
|
|
2143
|
+
this.logger.info({ health: { current: currentHealth, new: health } }, `Restarting service "${serviceId}" worker ${i} to update config health heap...`)
|
|
2144
|
+
|
|
2145
|
+
const worker = await this.#getWorkerById(serviceId, i)
|
|
2146
|
+
if (health.maxHeapTotal) { worker[kConfig].health.maxHeapTotal = health.maxHeapTotal }
|
|
2147
|
+
if (health.maxYoungGeneration) { worker[kConfig].health.maxYoungGeneration = health.maxYoungGeneration }
|
|
2148
|
+
|
|
2149
|
+
await this.#replaceWorker(config, serviceConfig, currentWorkers, serviceId, i, worker)
|
|
2150
|
+
report.updated.push(i)
|
|
2151
|
+
this.logger.info({ health: { current: currentHealth, new: health } }, `Restarted service "${serviceId}" worker ${i}`)
|
|
2152
|
+
}
|
|
2153
|
+
report.success = true
|
|
2154
|
+
} catch (err) {
|
|
2155
|
+
if (report.updated.length < 1) {
|
|
2156
|
+
this.logger.error({ err }, 'Cannot update service health heap, no worker updated')
|
|
2157
|
+
await this.#updateServiceConfigHealth(serviceId, currentHealth)
|
|
2158
|
+
} else {
|
|
2159
|
+
this.logger.error({ err }, `Cannot update service health heap, updated workers: ${report.updated.length} out of ${currentWorkers}`)
|
|
2160
|
+
}
|
|
2161
|
+
report.success = false
|
|
2162
|
+
}
|
|
2163
|
+
return report
|
|
2164
|
+
}
|
|
2165
|
+
|
|
2166
|
+
async #updateServiceWorkers (serviceId, config, serviceConfig, workers, currentWorkers) {
|
|
2167
|
+
const report = {
|
|
2168
|
+
current: currentWorkers,
|
|
2169
|
+
new: workers
|
|
2170
|
+
}
|
|
2171
|
+
if (currentWorkers < workers) {
|
|
2172
|
+
report.started = []
|
|
2173
|
+
try {
|
|
2174
|
+
await this.#updateServiceConfigWorkers(serviceId, workers)
|
|
1916
2175
|
for (let i = currentWorkers; i < workers; i++) {
|
|
1917
2176
|
await this.#setupWorker(config, serviceConfig, workers, serviceId, i)
|
|
1918
2177
|
await this.#startWorker(config, serviceConfig, workers, serviceId, i, false, 0)
|
|
2178
|
+
report.started.push(i)
|
|
1919
2179
|
}
|
|
1920
|
-
|
|
2180
|
+
report.success = true
|
|
2181
|
+
} catch (err) {
|
|
2182
|
+
if (report.started.length < 1) {
|
|
2183
|
+
this.logger.error({ err }, 'Cannot start service workers, no worker started')
|
|
2184
|
+
await this.#updateServiceConfigWorkers(serviceId, currentWorkers)
|
|
2185
|
+
} else {
|
|
2186
|
+
this.logger.error({ err }, `Cannot start service workers, started workers: ${report.started.length} out of ${workers}`)
|
|
2187
|
+
await this.#updateServiceConfigWorkers(serviceId, currentWorkers + report.started.length)
|
|
2188
|
+
}
|
|
2189
|
+
report.success = false
|
|
2190
|
+
}
|
|
2191
|
+
} else {
|
|
2192
|
+
// keep the current workers count until all the service workers are all stopped
|
|
2193
|
+
report.stopped = []
|
|
2194
|
+
try {
|
|
1921
2195
|
for (let i = currentWorkers - 1; i >= workers; i--) {
|
|
1922
|
-
|
|
1923
|
-
await
|
|
2196
|
+
const worker = await this.#getWorkerById(serviceId, i, false, false)
|
|
2197
|
+
await sendViaITC(worker, 'removeFromMesh')
|
|
2198
|
+
await this.#stopWorker(currentWorkers, serviceId, i, false, worker)
|
|
2199
|
+
report.stopped.push(i)
|
|
2200
|
+
}
|
|
2201
|
+
await this.#updateServiceConfigWorkers(serviceId, workers)
|
|
2202
|
+
report.success = true
|
|
2203
|
+
} catch (err) {
|
|
2204
|
+
if (report.stopped.length < 1) {
|
|
2205
|
+
this.logger.error({ err }, 'Cannot stop service workers, no worker stopped')
|
|
2206
|
+
} else {
|
|
2207
|
+
this.logger.error({ err }, `Cannot stop service workers, stopped workers: ${report.stopped.length} out of ${workers}`)
|
|
2208
|
+
await this.#updateServiceConfigWorkers(serviceId, currentWorkers - report.stopped)
|
|
1924
2209
|
}
|
|
1925
|
-
|
|
2210
|
+
report.success = false
|
|
1926
2211
|
}
|
|
1927
2212
|
}
|
|
2213
|
+
return report
|
|
1928
2214
|
}
|
|
1929
2215
|
}
|
|
1930
2216
|
|
|
@@ -164,14 +164,16 @@ async function getDispatcherOpts (undiciConfig) {
|
|
|
164
164
|
|
|
165
165
|
function createThreadInterceptor (runtimeConfig) {
|
|
166
166
|
const telemetry = runtimeConfig.telemetry
|
|
167
|
-
|
|
167
|
+
|
|
168
|
+
const telemetryHooks = telemetry ? createTelemetryThreadInterceptorHooks() : {}
|
|
169
|
+
|
|
168
170
|
const threadDispatcher = wire({
|
|
169
171
|
// Specifying the domain is critical to avoid flooding the DNS
|
|
170
172
|
// with requests for a domain that's never going to exist.
|
|
171
173
|
domain: '.plt.local',
|
|
172
174
|
port: parentPort,
|
|
173
175
|
timeout: runtimeConfig.serviceTimeout,
|
|
174
|
-
...
|
|
176
|
+
...telemetryHooks,
|
|
175
177
|
})
|
|
176
178
|
return threadDispatcher
|
|
177
179
|
}
|
package/lib/worker/symbols.js
CHANGED
|
@@ -9,6 +9,7 @@ const kITC = Symbol.for('plt.runtime.itc')
|
|
|
9
9
|
const kHealthCheckTimer = Symbol.for('plt.runtime.worker.healthCheckTimer')
|
|
10
10
|
const kWorkerStatus = Symbol('plt.runtime.worker.status')
|
|
11
11
|
const kInterceptors = Symbol.for('plt.runtime.worker.interceptors')
|
|
12
|
+
const kLastELU = Symbol.for('plt.runtime.worker.lastELU')
|
|
12
13
|
|
|
13
14
|
// This string marker should be safe to use since it belongs to Unicode private area
|
|
14
15
|
const kStderrMarker = '\ue002'
|
|
@@ -21,6 +22,7 @@ module.exports = {
|
|
|
21
22
|
kWorkerId,
|
|
22
23
|
kITC,
|
|
23
24
|
kHealthCheckTimer,
|
|
25
|
+
kLastELU,
|
|
24
26
|
kWorkerStatus,
|
|
25
27
|
kStderrMarker,
|
|
26
28
|
kInterceptors
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/runtime",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.70.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -37,12 +37,12 @@
|
|
|
37
37
|
"typescript": "^5.5.4",
|
|
38
38
|
"undici-oidc-interceptor": "^0.5.0",
|
|
39
39
|
"why-is-node-running": "^2.2.2",
|
|
40
|
-
"@platformatic/composer": "2.
|
|
41
|
-
"@platformatic/db": "2.
|
|
42
|
-
"@platformatic/node": "2.
|
|
43
|
-
"@platformatic/service": "2.
|
|
44
|
-
"@platformatic/sql-graphql": "2.
|
|
45
|
-
"@platformatic/sql-mapper": "2.
|
|
40
|
+
"@platformatic/composer": "2.70.0",
|
|
41
|
+
"@platformatic/db": "2.70.0",
|
|
42
|
+
"@platformatic/node": "2.70.0",
|
|
43
|
+
"@platformatic/service": "2.70.0",
|
|
44
|
+
"@platformatic/sql-graphql": "2.70.0",
|
|
45
|
+
"@platformatic/sql-mapper": "2.70.0"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@fastify/accepts": "^5.0.0",
|
|
@@ -50,7 +50,6 @@
|
|
|
50
50
|
"@fastify/websocket": "^11.0.0",
|
|
51
51
|
"@hapi/topo": "^6.0.2",
|
|
52
52
|
"@opentelemetry/api": "^1.8.0",
|
|
53
|
-
"@platformatic/http-metrics": "^0.2.1",
|
|
54
53
|
"@platformatic/undici-cache-memory": "^0.8.1",
|
|
55
54
|
"@watchable/unpromise": "^1.0.2",
|
|
56
55
|
"change-case-all": "^2.1.0",
|
|
@@ -75,23 +74,24 @@
|
|
|
75
74
|
"sonic-boom": "^4.2.0",
|
|
76
75
|
"tail-file-stream": "^0.2.0",
|
|
77
76
|
"undici": "^7.0.0",
|
|
78
|
-
"undici-thread-interceptor": "^0.
|
|
77
|
+
"undici-thread-interceptor": "^0.14.0",
|
|
79
78
|
"ws": "^8.16.0",
|
|
80
|
-
"@platformatic/basic": "2.
|
|
81
|
-
"@platformatic/
|
|
82
|
-
"@platformatic/
|
|
83
|
-
"@platformatic/itc": "2.
|
|
84
|
-
"@platformatic/telemetry": "2.
|
|
85
|
-
"@platformatic/ts-compiler": "2.
|
|
86
|
-
"@platformatic/utils": "2.
|
|
79
|
+
"@platformatic/basic": "2.70.0",
|
|
80
|
+
"@platformatic/generators": "2.70.0",
|
|
81
|
+
"@platformatic/metrics": "2.70.0",
|
|
82
|
+
"@platformatic/itc": "2.70.0",
|
|
83
|
+
"@platformatic/telemetry": "2.70.0",
|
|
84
|
+
"@platformatic/ts-compiler": "2.70.0",
|
|
85
|
+
"@platformatic/utils": "2.70.0",
|
|
86
|
+
"@platformatic/config": "2.70.0"
|
|
87
87
|
},
|
|
88
88
|
"scripts": {
|
|
89
|
-
"test": "pnpm run lint && borp --concurrency=1 --timeout=
|
|
89
|
+
"test": "pnpm run lint && borp --concurrency=1 --timeout=1200000 && tsd",
|
|
90
90
|
"test:main": "borp --concurrency=1 --timeout=300000 test/*.test.js test/*.test.mjs test/versions/*.test.js test/versions/*.test.mjs",
|
|
91
91
|
"test:api": "borp --concurrency=1 --timeout=300000 test/api/*.test.js test/api/*.test.mjs test/management-api/*.test.js test/management-api/*.test.mjs",
|
|
92
92
|
"test:cli": "borp --concurrency=1 --timeout=300000 test/cli/*.test.js test/cli/*.test.mjs test/cli/**/*.test.js test/cli/**/*.test.mjs",
|
|
93
93
|
"test:start": "borp --concurrency=1 --timeout=300000 test/start/*.test.js test/start/*.test.mjs",
|
|
94
|
-
"test:multiple-workers": "borp --concurrency=1 --timeout=
|
|
94
|
+
"test:multiple-workers": "borp --concurrency=1 --timeout=1200000 test/multiple-workers/*.test.js test/multiple-workers/*.test.mjs",
|
|
95
95
|
"test:types": "tsd",
|
|
96
96
|
"coverage": "pnpm run lint && borp -X fixtures -X test -C --concurrency=1 --timeout=300000 && tsd",
|
|
97
97
|
"gen-schema": "node lib/schema.js > schema.json",
|
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.70.0.json",
|
|
3
3
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -76,6 +76,7 @@
|
|
|
76
76
|
},
|
|
77
77
|
"health": {
|
|
78
78
|
"type": "object",
|
|
79
|
+
"default": {},
|
|
79
80
|
"properties": {
|
|
80
81
|
"enabled": {
|
|
81
82
|
"anyOf": [
|
|
@@ -156,8 +157,15 @@
|
|
|
156
157
|
]
|
|
157
158
|
},
|
|
158
159
|
"maxYoungGeneration": {
|
|
159
|
-
"
|
|
160
|
-
|
|
160
|
+
"anyOf": [
|
|
161
|
+
{
|
|
162
|
+
"type": "number",
|
|
163
|
+
"minimum": 0
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"type": "string"
|
|
167
|
+
}
|
|
168
|
+
]
|
|
161
169
|
}
|
|
162
170
|
},
|
|
163
171
|
"additionalProperties": false
|
|
@@ -244,6 +252,7 @@
|
|
|
244
252
|
},
|
|
245
253
|
"health": {
|
|
246
254
|
"type": "object",
|
|
255
|
+
"default": {},
|
|
247
256
|
"properties": {
|
|
248
257
|
"enabled": {
|
|
249
258
|
"anyOf": [
|
|
@@ -324,8 +333,15 @@
|
|
|
324
333
|
]
|
|
325
334
|
},
|
|
326
335
|
"maxYoungGeneration": {
|
|
327
|
-
"
|
|
328
|
-
|
|
336
|
+
"anyOf": [
|
|
337
|
+
{
|
|
338
|
+
"type": "number",
|
|
339
|
+
"minimum": 0
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
"type": "string"
|
|
343
|
+
}
|
|
344
|
+
]
|
|
329
345
|
}
|
|
330
346
|
},
|
|
331
347
|
"additionalProperties": false
|
|
@@ -477,6 +493,7 @@
|
|
|
477
493
|
},
|
|
478
494
|
"health": {
|
|
479
495
|
"type": "object",
|
|
496
|
+
"default": {},
|
|
480
497
|
"properties": {
|
|
481
498
|
"enabled": {
|
|
482
499
|
"anyOf": [
|
|
@@ -557,8 +574,15 @@
|
|
|
557
574
|
]
|
|
558
575
|
},
|
|
559
576
|
"maxYoungGeneration": {
|
|
560
|
-
"
|
|
561
|
-
|
|
577
|
+
"anyOf": [
|
|
578
|
+
{
|
|
579
|
+
"type": "number",
|
|
580
|
+
"minimum": 0
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
"type": "string"
|
|
584
|
+
}
|
|
585
|
+
]
|
|
562
586
|
}
|
|
563
587
|
},
|
|
564
588
|
"additionalProperties": false
|
|
@@ -976,7 +1000,6 @@
|
|
|
976
1000
|
"default": {},
|
|
977
1001
|
"properties": {
|
|
978
1002
|
"enabled": {
|
|
979
|
-
"default": true,
|
|
980
1003
|
"anyOf": [
|
|
981
1004
|
{
|
|
982
1005
|
"type": "boolean"
|
|
@@ -984,10 +1007,10 @@
|
|
|
984
1007
|
{
|
|
985
1008
|
"type": "string"
|
|
986
1009
|
}
|
|
987
|
-
]
|
|
1010
|
+
],
|
|
1011
|
+
"default": true
|
|
988
1012
|
},
|
|
989
1013
|
"interval": {
|
|
990
|
-
"default": 30000,
|
|
991
1014
|
"anyOf": [
|
|
992
1015
|
{
|
|
993
1016
|
"type": "number",
|
|
@@ -996,10 +1019,10 @@
|
|
|
996
1019
|
{
|
|
997
1020
|
"type": "string"
|
|
998
1021
|
}
|
|
999
|
-
]
|
|
1022
|
+
],
|
|
1023
|
+
"default": 30000
|
|
1000
1024
|
},
|
|
1001
1025
|
"gracePeriod": {
|
|
1002
|
-
"default": 30000,
|
|
1003
1026
|
"anyOf": [
|
|
1004
1027
|
{
|
|
1005
1028
|
"type": "number",
|
|
@@ -1008,10 +1031,10 @@
|
|
|
1008
1031
|
{
|
|
1009
1032
|
"type": "string"
|
|
1010
1033
|
}
|
|
1011
|
-
]
|
|
1034
|
+
],
|
|
1035
|
+
"default": 30000
|
|
1012
1036
|
},
|
|
1013
1037
|
"maxUnhealthyChecks": {
|
|
1014
|
-
"default": 10,
|
|
1015
1038
|
"anyOf": [
|
|
1016
1039
|
{
|
|
1017
1040
|
"type": "number",
|
|
@@ -1020,10 +1043,10 @@
|
|
|
1020
1043
|
{
|
|
1021
1044
|
"type": "string"
|
|
1022
1045
|
}
|
|
1023
|
-
]
|
|
1046
|
+
],
|
|
1047
|
+
"default": 10
|
|
1024
1048
|
},
|
|
1025
1049
|
"maxELU": {
|
|
1026
|
-
"default": 0.99,
|
|
1027
1050
|
"anyOf": [
|
|
1028
1051
|
{
|
|
1029
1052
|
"type": "number",
|
|
@@ -1033,10 +1056,10 @@
|
|
|
1033
1056
|
{
|
|
1034
1057
|
"type": "string"
|
|
1035
1058
|
}
|
|
1036
|
-
]
|
|
1059
|
+
],
|
|
1060
|
+
"default": 0.99
|
|
1037
1061
|
},
|
|
1038
1062
|
"maxHeapUsed": {
|
|
1039
|
-
"default": 0.99,
|
|
1040
1063
|
"anyOf": [
|
|
1041
1064
|
{
|
|
1042
1065
|
"type": "number",
|
|
@@ -1046,10 +1069,10 @@
|
|
|
1046
1069
|
{
|
|
1047
1070
|
"type": "string"
|
|
1048
1071
|
}
|
|
1049
|
-
]
|
|
1072
|
+
],
|
|
1073
|
+
"default": 0.99
|
|
1050
1074
|
},
|
|
1051
1075
|
"maxHeapTotal": {
|
|
1052
|
-
"default": 4294967296,
|
|
1053
1076
|
"anyOf": [
|
|
1054
1077
|
{
|
|
1055
1078
|
"type": "number",
|
|
@@ -1058,11 +1081,19 @@
|
|
|
1058
1081
|
{
|
|
1059
1082
|
"type": "string"
|
|
1060
1083
|
}
|
|
1061
|
-
]
|
|
1084
|
+
],
|
|
1085
|
+
"default": 4294967296
|
|
1062
1086
|
},
|
|
1063
1087
|
"maxYoungGeneration": {
|
|
1064
|
-
"
|
|
1065
|
-
|
|
1088
|
+
"anyOf": [
|
|
1089
|
+
{
|
|
1090
|
+
"type": "number",
|
|
1091
|
+
"minimum": 0
|
|
1092
|
+
},
|
|
1093
|
+
{
|
|
1094
|
+
"type": "string"
|
|
1095
|
+
}
|
|
1096
|
+
]
|
|
1066
1097
|
}
|
|
1067
1098
|
},
|
|
1068
1099
|
"additionalProperties": false
|