@platformatic/runtime 3.7.0 → 3.8.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 +18 -0
- package/lib/runtime.js +141 -1
- package/lib/scaling-algorithm.js +179 -0
- package/lib/schema.js +12 -0
- package/package.json +15 -15
- package/schema.json +68 -3
package/config.d.ts
CHANGED
|
@@ -332,6 +332,24 @@ export type PlatformaticRuntimeConfig = {
|
|
|
332
332
|
[k: string]: unknown;
|
|
333
333
|
};
|
|
334
334
|
};
|
|
335
|
+
verticalScaler?: {
|
|
336
|
+
enabled?: boolean;
|
|
337
|
+
maxTotalWorkers?: number;
|
|
338
|
+
minWorkers?: number;
|
|
339
|
+
maxWorkers?: number;
|
|
340
|
+
scaleUpELU?: number;
|
|
341
|
+
scaleDownELU?: number;
|
|
342
|
+
minELUDiff?: number;
|
|
343
|
+
timeWindowSec?: number;
|
|
344
|
+
cooldownSec?: number;
|
|
345
|
+
scaleIntervalSec?: number;
|
|
346
|
+
applications?: {
|
|
347
|
+
[k: string]: {
|
|
348
|
+
minWorkers?: number;
|
|
349
|
+
maxWorkers?: number;
|
|
350
|
+
};
|
|
351
|
+
};
|
|
352
|
+
};
|
|
335
353
|
inspectorOptions?: {
|
|
336
354
|
host?: string;
|
|
337
355
|
port?: number;
|
package/lib/runtime.js
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
kTimeout,
|
|
10
10
|
parseMemorySize
|
|
11
11
|
} from '@platformatic/foundation'
|
|
12
|
+
import os from 'node:os'
|
|
12
13
|
import { ITC } from '@platformatic/itc'
|
|
13
14
|
import fastify from 'fastify'
|
|
14
15
|
import { EventEmitter, once } from 'node:events'
|
|
@@ -44,6 +45,7 @@ import { createSharedStore } from './shared-http-cache.js'
|
|
|
44
45
|
import { version } from './version.js'
|
|
45
46
|
import { sendViaITC, waitEventFromITC } from './worker/itc.js'
|
|
46
47
|
import { RoundRobinMap } from './worker/round-robin-map.js'
|
|
48
|
+
import ScalingAlgorithm from './scaling-algorithm.js'
|
|
47
49
|
import {
|
|
48
50
|
kApplicationId,
|
|
49
51
|
kConfig,
|
|
@@ -179,7 +181,7 @@ export class Runtime extends EventEmitter {
|
|
|
179
181
|
|
|
180
182
|
const workersConfig = []
|
|
181
183
|
for (const application of config.applications) {
|
|
182
|
-
const count = application.workers ?? this.#config.workers
|
|
184
|
+
const count = application.workers ?? this.#config.workers ?? 1
|
|
183
185
|
if (count > 1 && application.entrypoint && !features.node.reusePort) {
|
|
184
186
|
this.logger.warn(
|
|
185
187
|
`"${application.id}" is set as the entrypoint, but reusePort is not available in your OS; setting workers to 1 instead of ${count}`
|
|
@@ -274,6 +276,10 @@ export class Runtime extends EventEmitter {
|
|
|
274
276
|
this.startCollectingMetrics()
|
|
275
277
|
}
|
|
276
278
|
|
|
279
|
+
if (this.#config.verticalScaler?.enabled) {
|
|
280
|
+
this.#setupVerticalScaler()
|
|
281
|
+
}
|
|
282
|
+
|
|
277
283
|
this.#showUrl()
|
|
278
284
|
return this.#url
|
|
279
285
|
}
|
|
@@ -2434,4 +2440,138 @@ export class Runtime extends EventEmitter {
|
|
|
2434
2440
|
throw new MissingPprofCapture()
|
|
2435
2441
|
}
|
|
2436
2442
|
}
|
|
2443
|
+
|
|
2444
|
+
#setupVerticalScaler () {
|
|
2445
|
+
const isWorkersFixed = this.#config.workers !== undefined
|
|
2446
|
+
if (isWorkersFixed) return
|
|
2447
|
+
|
|
2448
|
+
const scalerConfig = this.#config.verticalScaler
|
|
2449
|
+
|
|
2450
|
+
scalerConfig.maxTotalWorkers ??= os.availableParallelism()
|
|
2451
|
+
scalerConfig.maxWorkers ??= scalerConfig.maxTotalWorkers
|
|
2452
|
+
scalerConfig.minWorkers ??= 1
|
|
2453
|
+
scalerConfig.cooldownSec ??= 60
|
|
2454
|
+
scalerConfig.scaleUpELU ??= 0.8
|
|
2455
|
+
scalerConfig.scaleDownELU ??= 0.2
|
|
2456
|
+
scalerConfig.minELUDiff ??= 0.2
|
|
2457
|
+
scalerConfig.scaleIntervalSec ??= 60
|
|
2458
|
+
scalerConfig.timeWindowSec ??= 60
|
|
2459
|
+
scalerConfig.applications ??= {}
|
|
2460
|
+
|
|
2461
|
+
const maxTotalWorkers = scalerConfig.maxTotalWorkers
|
|
2462
|
+
const maxWorkers = scalerConfig.maxWorkers
|
|
2463
|
+
const minWorkers = scalerConfig.minWorkers
|
|
2464
|
+
const cooldown = scalerConfig.cooldownSec
|
|
2465
|
+
const scaleUpELU = scalerConfig.scaleUpELU
|
|
2466
|
+
const scaleDownELU = scalerConfig.scaleDownELU
|
|
2467
|
+
const minELUDiff = scalerConfig.minELUDiff
|
|
2468
|
+
const scaleIntervalSec = scalerConfig.scaleIntervalSec
|
|
2469
|
+
const timeWindowSec = scalerConfig.timeWindowSec
|
|
2470
|
+
const applicationsConfigs = scalerConfig.applications
|
|
2471
|
+
|
|
2472
|
+
for (const application of this.#config.applications) {
|
|
2473
|
+
if (application.entrypoint && !features.node.reusePort) {
|
|
2474
|
+
applicationsConfigs[application.id] = {
|
|
2475
|
+
minWorkers: 1,
|
|
2476
|
+
maxWorkers: 1
|
|
2477
|
+
}
|
|
2478
|
+
continue
|
|
2479
|
+
}
|
|
2480
|
+
if (application.workers !== undefined) {
|
|
2481
|
+
applicationsConfigs[application.id] = {
|
|
2482
|
+
minWorkers: application.workers,
|
|
2483
|
+
maxWorkers: application.workers
|
|
2484
|
+
}
|
|
2485
|
+
continue
|
|
2486
|
+
}
|
|
2487
|
+
|
|
2488
|
+
applicationsConfigs[application.id] ??= {}
|
|
2489
|
+
applicationsConfigs[application.id].minWorkers ??= minWorkers
|
|
2490
|
+
applicationsConfigs[application.id].maxWorkers ??= maxWorkers
|
|
2491
|
+
}
|
|
2492
|
+
|
|
2493
|
+
for (const applicationId in applicationsConfigs) {
|
|
2494
|
+
const application = this.#config.applications.find(
|
|
2495
|
+
app => app.id === applicationId
|
|
2496
|
+
)
|
|
2497
|
+
if (!application) {
|
|
2498
|
+
delete applicationsConfigs[applicationId]
|
|
2499
|
+
|
|
2500
|
+
this.logger.warn(
|
|
2501
|
+
`Vertical scaler configuration has a configuration for non-existing application "${applicationId}"`
|
|
2502
|
+
)
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
|
|
2506
|
+
const scalingAlgorithm = new ScalingAlgorithm({
|
|
2507
|
+
maxTotalWorkers,
|
|
2508
|
+
scaleUpELU,
|
|
2509
|
+
scaleDownELU,
|
|
2510
|
+
minELUDiff,
|
|
2511
|
+
timeWindowSec,
|
|
2512
|
+
applications: applicationsConfigs
|
|
2513
|
+
})
|
|
2514
|
+
|
|
2515
|
+
this.on('application:worker:health', async (healthInfo) => {
|
|
2516
|
+
if (!healthInfo) {
|
|
2517
|
+
this.logger.error('No health info received')
|
|
2518
|
+
return
|
|
2519
|
+
}
|
|
2520
|
+
|
|
2521
|
+
scalingAlgorithm.addWorkerHealthInfo(healthInfo)
|
|
2522
|
+
|
|
2523
|
+
if (healthInfo.currentHealth.elu > scaleUpELU) {
|
|
2524
|
+
await checkForScaling()
|
|
2525
|
+
}
|
|
2526
|
+
})
|
|
2527
|
+
|
|
2528
|
+
let isScaling = false
|
|
2529
|
+
let lastScaling = 0
|
|
2530
|
+
|
|
2531
|
+
const checkForScaling = async () => {
|
|
2532
|
+
const isInCooldown = Date.now() < lastScaling + cooldown * 1000
|
|
2533
|
+
if (isScaling || isInCooldown) return
|
|
2534
|
+
isScaling = true
|
|
2535
|
+
|
|
2536
|
+
try {
|
|
2537
|
+
const workersInfo = await this.getWorkers()
|
|
2538
|
+
|
|
2539
|
+
const appsWorkersInfo = {}
|
|
2540
|
+
for (const worker of Object.values(workersInfo)) {
|
|
2541
|
+
if (worker.status === 'exited') continue
|
|
2542
|
+
|
|
2543
|
+
const applicationId = worker.application
|
|
2544
|
+
appsWorkersInfo[applicationId] ??= 0
|
|
2545
|
+
appsWorkersInfo[applicationId]++
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2548
|
+
const recommendations = scalingAlgorithm.getRecommendations(appsWorkersInfo)
|
|
2549
|
+
if (recommendations.length > 0) {
|
|
2550
|
+
await applyRecommendations(recommendations)
|
|
2551
|
+
}
|
|
2552
|
+
} catch (err) {
|
|
2553
|
+
this.logger.error({ err }, 'Failed to scale applications')
|
|
2554
|
+
} finally {
|
|
2555
|
+
isScaling = false
|
|
2556
|
+
lastScaling = Date.now()
|
|
2557
|
+
}
|
|
2558
|
+
}
|
|
2559
|
+
|
|
2560
|
+
const applyRecommendations = async (recommendations) => {
|
|
2561
|
+
const resourcesUpdates = []
|
|
2562
|
+
for (const recommendation of recommendations) {
|
|
2563
|
+
const { applicationId, workersCount, direction } = recommendation
|
|
2564
|
+
this.logger.info(`Scaling ${direction} the "${applicationId}" app to ${workersCount} workers`)
|
|
2565
|
+
|
|
2566
|
+
resourcesUpdates.push({
|
|
2567
|
+
application: applicationId,
|
|
2568
|
+
workers: workersCount
|
|
2569
|
+
})
|
|
2570
|
+
}
|
|
2571
|
+
await this.updateApplicationsResources(resourcesUpdates)
|
|
2572
|
+
}
|
|
2573
|
+
|
|
2574
|
+
// Interval for periodic scaling checks
|
|
2575
|
+
setInterval(checkForScaling, scaleIntervalSec * 1000).unref()
|
|
2576
|
+
}
|
|
2437
2577
|
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
class ScalingAlgorithm {
|
|
2
|
+
#scaleUpELU
|
|
3
|
+
#scaleDownELU
|
|
4
|
+
#maxTotalWorkers
|
|
5
|
+
#timeWindowSec
|
|
6
|
+
#appsELUs
|
|
7
|
+
#minELUDiff
|
|
8
|
+
#appsConfigs
|
|
9
|
+
|
|
10
|
+
constructor (options = {}) {
|
|
11
|
+
this.#scaleUpELU = options.scaleUpELU ?? 0.8
|
|
12
|
+
this.#scaleDownELU = options.scaleDownELU ?? 0.2
|
|
13
|
+
this.#maxTotalWorkers = options.maxTotalWorkers
|
|
14
|
+
this.#minELUDiff = options.minELUDiff ?? 0.2
|
|
15
|
+
this.#timeWindowSec = options.timeWindowSec ?? 60
|
|
16
|
+
this.#appsConfigs = options.applications ?? {}
|
|
17
|
+
|
|
18
|
+
this.#appsELUs = {}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
addWorkerHealthInfo (healthInfo) {
|
|
22
|
+
const workerId = healthInfo.id
|
|
23
|
+
const applicationId = healthInfo.application
|
|
24
|
+
const elu = healthInfo.currentHealth.elu
|
|
25
|
+
const timestamp = Date.now()
|
|
26
|
+
|
|
27
|
+
if (!this.#appsELUs[applicationId]) {
|
|
28
|
+
this.#appsELUs[applicationId] = {}
|
|
29
|
+
}
|
|
30
|
+
if (!this.#appsELUs[applicationId][workerId]) {
|
|
31
|
+
this.#appsELUs[applicationId][workerId] = []
|
|
32
|
+
}
|
|
33
|
+
this.#appsELUs[applicationId][workerId].push({ elu, timestamp })
|
|
34
|
+
this.#removeOutdatedAppELUs(applicationId)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
getRecommendations (appsWorkersInfo) {
|
|
38
|
+
let totalWorkersCount = 0
|
|
39
|
+
let appsInfo = []
|
|
40
|
+
|
|
41
|
+
for (const applicationId in appsWorkersInfo) {
|
|
42
|
+
const workersCount = appsWorkersInfo[applicationId]
|
|
43
|
+
const elu = this.#calculateAppAvgELU(applicationId)
|
|
44
|
+
appsInfo.push({ applicationId, workersCount, elu })
|
|
45
|
+
totalWorkersCount += workersCount
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
appsInfo = appsInfo.sort(
|
|
49
|
+
(app1, app2) => {
|
|
50
|
+
if (app1.elu > app2.elu) return 1
|
|
51
|
+
if (app1.elu < app2.elu) return -1
|
|
52
|
+
if (app1.workersCount < app2.workersCount) return 1
|
|
53
|
+
if (app1.workersCount > app2.workersCount) return -1
|
|
54
|
+
return 0
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
const recommendations = []
|
|
59
|
+
|
|
60
|
+
for (const { applicationId, elu, workersCount } of appsInfo) {
|
|
61
|
+
const appMinWorkers = this.#appsConfigs[applicationId]?.minWorkers ?? 1
|
|
62
|
+
|
|
63
|
+
if (elu < this.#scaleDownELU && workersCount > appMinWorkers) {
|
|
64
|
+
recommendations.push({
|
|
65
|
+
applicationId,
|
|
66
|
+
workersCount: workersCount - 1,
|
|
67
|
+
direction: 'down'
|
|
68
|
+
})
|
|
69
|
+
totalWorkersCount--
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
for (const scaleUpCandidate of appsInfo.toReversed()) {
|
|
74
|
+
if (scaleUpCandidate.elu < this.#scaleUpELU) break
|
|
75
|
+
|
|
76
|
+
const { applicationId, workersCount } = scaleUpCandidate
|
|
77
|
+
|
|
78
|
+
const appMaxWorkers = this.#appsConfigs[applicationId]?.maxWorkers ?? this.#maxTotalWorkers
|
|
79
|
+
if (workersCount >= appMaxWorkers) continue
|
|
80
|
+
|
|
81
|
+
if (totalWorkersCount >= this.#maxTotalWorkers) {
|
|
82
|
+
let scaleDownCandidate = null
|
|
83
|
+
for (const app of appsInfo) {
|
|
84
|
+
const appMinWorkers = this.#appsConfigs[app.applicationId]?.minWorkers ?? 1
|
|
85
|
+
if (app.workersCount > appMinWorkers) {
|
|
86
|
+
scaleDownCandidate = app
|
|
87
|
+
break
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (scaleDownCandidate) {
|
|
92
|
+
const eluDiff = scaleUpCandidate.elu - scaleDownCandidate.elu
|
|
93
|
+
const workersDiff = scaleDownCandidate.workersCount - scaleUpCandidate.workersCount
|
|
94
|
+
|
|
95
|
+
if (eluDiff >= this.#minELUDiff || workersDiff >= 2) {
|
|
96
|
+
recommendations.push({
|
|
97
|
+
applicationId: scaleDownCandidate.applicationId,
|
|
98
|
+
workersCount: scaleDownCandidate.workersCount - 1,
|
|
99
|
+
direction: 'down'
|
|
100
|
+
})
|
|
101
|
+
recommendations.push({
|
|
102
|
+
applicationId,
|
|
103
|
+
workersCount: workersCount + 1,
|
|
104
|
+
direction: 'up'
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
recommendations.push({
|
|
110
|
+
applicationId,
|
|
111
|
+
workersCount: workersCount + 1,
|
|
112
|
+
direction: 'up'
|
|
113
|
+
})
|
|
114
|
+
totalWorkersCount++
|
|
115
|
+
}
|
|
116
|
+
break
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return recommendations
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
#calculateAppAvgELU (applicationId) {
|
|
123
|
+
this.#removeOutdatedAppELUs(applicationId)
|
|
124
|
+
|
|
125
|
+
const appELUs = this.#appsELUs[applicationId]
|
|
126
|
+
if (!appELUs) return 0
|
|
127
|
+
|
|
128
|
+
let eluSum = 0
|
|
129
|
+
let eluCount = 0
|
|
130
|
+
|
|
131
|
+
for (const workerId in appELUs) {
|
|
132
|
+
const workerELUs = appELUs[workerId]
|
|
133
|
+
const workerELUSum = workerELUs.reduce(
|
|
134
|
+
(sum, workerELU) => sum + workerELU.elu, 0
|
|
135
|
+
)
|
|
136
|
+
eluSum += workerELUSum / workerELUs.length
|
|
137
|
+
eluCount++
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (eluCount === 0) return 0
|
|
141
|
+
|
|
142
|
+
return Math.round(eluSum / eluCount * 100) / 100
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
#removeOutdatedAppELUs (applicationId) {
|
|
146
|
+
const appELUs = this.#appsELUs[applicationId]
|
|
147
|
+
if (!appELUs) return
|
|
148
|
+
|
|
149
|
+
const now = Date.now()
|
|
150
|
+
|
|
151
|
+
for (const workerId in appELUs) {
|
|
152
|
+
const workerELUs = appELUs[workerId]
|
|
153
|
+
|
|
154
|
+
let firstValidIndex = -1
|
|
155
|
+
for (let i = 0; i < workerELUs.length; i++) {
|
|
156
|
+
const timestamp = workerELUs[i].timestamp
|
|
157
|
+
if (timestamp >= now - this.#timeWindowSec * 1000) {
|
|
158
|
+
firstValidIndex = i
|
|
159
|
+
break
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (firstValidIndex > 0) {
|
|
164
|
+
// Remove all outdated entries before the first valid one
|
|
165
|
+
workerELUs.splice(0, firstValidIndex)
|
|
166
|
+
} else if (firstValidIndex === -1) {
|
|
167
|
+
// All entries are outdated, clear the array
|
|
168
|
+
workerELUs.length = 0
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// If there are no more workerELUs, remove the workerId
|
|
172
|
+
if (workerELUs.length === 0) {
|
|
173
|
+
delete appELUs[workerId]
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export default ScalingAlgorithm
|
package/lib/schema.js
CHANGED
|
@@ -16,6 +16,18 @@ const runtimeLogger = {
|
|
|
16
16
|
|
|
17
17
|
schemaComponents.runtimeProperties.logger = runtimeLogger
|
|
18
18
|
|
|
19
|
+
schemaComponents.runtimeProperties.verticalScaler.properties.applications = {
|
|
20
|
+
type: 'object',
|
|
21
|
+
additionalProperties: {
|
|
22
|
+
type: 'object',
|
|
23
|
+
properties: {
|
|
24
|
+
minWorkers: { type: 'number', minimum: 1 },
|
|
25
|
+
maxWorkers: { type: 'number', minimum: 1 }
|
|
26
|
+
},
|
|
27
|
+
additionalProperties: false
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
19
31
|
const platformaticRuntimeSchema = {
|
|
20
32
|
$id: `https://schemas.platformatic.dev/@platformatic/runtime/${version}.json`,
|
|
21
33
|
$schema: 'http://json-schema.org/draft-07/schema#',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/runtime",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -34,14 +34,14 @@
|
|
|
34
34
|
"typescript": "^5.5.4",
|
|
35
35
|
"undici-oidc-interceptor": "^0.5.0",
|
|
36
36
|
"why-is-node-running": "^2.2.2",
|
|
37
|
-
"@platformatic/composer": "3.
|
|
38
|
-
"@platformatic/db": "3.
|
|
39
|
-
"@platformatic/gateway": "3.
|
|
40
|
-
"@platformatic/node": "3.
|
|
41
|
-
"@platformatic/sql-graphql": "3.
|
|
42
|
-
"@platformatic/
|
|
43
|
-
"@platformatic/
|
|
44
|
-
"@platformatic/wattpm-pprof-capture": "3.
|
|
37
|
+
"@platformatic/composer": "3.8.0",
|
|
38
|
+
"@platformatic/db": "3.8.0",
|
|
39
|
+
"@platformatic/gateway": "3.8.0",
|
|
40
|
+
"@platformatic/node": "3.8.0",
|
|
41
|
+
"@platformatic/sql-graphql": "3.8.0",
|
|
42
|
+
"@platformatic/service": "3.8.0",
|
|
43
|
+
"@platformatic/sql-mapper": "3.8.0",
|
|
44
|
+
"@platformatic/wattpm-pprof-capture": "3.8.0"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@fastify/accepts": "^5.0.0",
|
|
@@ -71,12 +71,12 @@
|
|
|
71
71
|
"undici": "^7.0.0",
|
|
72
72
|
"undici-thread-interceptor": "^0.14.0",
|
|
73
73
|
"ws": "^8.16.0",
|
|
74
|
-
"@platformatic/
|
|
75
|
-
"@platformatic/
|
|
76
|
-
"@platformatic/itc": "3.
|
|
77
|
-
"@platformatic/metrics": "3.
|
|
78
|
-
"@platformatic/generators": "3.
|
|
79
|
-
"@platformatic/telemetry": "3.
|
|
74
|
+
"@platformatic/basic": "3.8.0",
|
|
75
|
+
"@platformatic/foundation": "3.8.0",
|
|
76
|
+
"@platformatic/itc": "3.8.0",
|
|
77
|
+
"@platformatic/metrics": "3.8.0",
|
|
78
|
+
"@platformatic/generators": "3.8.0",
|
|
79
|
+
"@platformatic/telemetry": "3.8.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.8.0.json",
|
|
3
3
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
4
|
"title": "Platformatic Runtime Config",
|
|
5
5
|
"type": "object",
|
|
@@ -923,8 +923,7 @@
|
|
|
923
923
|
{
|
|
924
924
|
"type": "string"
|
|
925
925
|
}
|
|
926
|
-
]
|
|
927
|
-
"default": 1
|
|
926
|
+
]
|
|
928
927
|
},
|
|
929
928
|
"workersRestartDelay": {
|
|
930
929
|
"anyOf": [
|
|
@@ -1828,6 +1827,72 @@
|
|
|
1828
1827
|
],
|
|
1829
1828
|
"additionalProperties": false
|
|
1830
1829
|
},
|
|
1830
|
+
"verticalScaler": {
|
|
1831
|
+
"type": "object",
|
|
1832
|
+
"properties": {
|
|
1833
|
+
"enabled": {
|
|
1834
|
+
"type": "boolean",
|
|
1835
|
+
"default": true
|
|
1836
|
+
},
|
|
1837
|
+
"maxTotalWorkers": {
|
|
1838
|
+
"type": "number",
|
|
1839
|
+
"minimum": 1
|
|
1840
|
+
},
|
|
1841
|
+
"minWorkers": {
|
|
1842
|
+
"type": "number",
|
|
1843
|
+
"minimum": 1
|
|
1844
|
+
},
|
|
1845
|
+
"maxWorkers": {
|
|
1846
|
+
"type": "number",
|
|
1847
|
+
"minimum": 1
|
|
1848
|
+
},
|
|
1849
|
+
"scaleUpELU": {
|
|
1850
|
+
"type": "number",
|
|
1851
|
+
"minimum": 0,
|
|
1852
|
+
"maximum": 1
|
|
1853
|
+
},
|
|
1854
|
+
"scaleDownELU": {
|
|
1855
|
+
"type": "number",
|
|
1856
|
+
"minimum": 0,
|
|
1857
|
+
"maximum": 1
|
|
1858
|
+
},
|
|
1859
|
+
"minELUDiff": {
|
|
1860
|
+
"type": "number",
|
|
1861
|
+
"minimum": 0,
|
|
1862
|
+
"maximum": 1
|
|
1863
|
+
},
|
|
1864
|
+
"timeWindowSec": {
|
|
1865
|
+
"type": "number",
|
|
1866
|
+
"minimum": 0
|
|
1867
|
+
},
|
|
1868
|
+
"cooldownSec": {
|
|
1869
|
+
"type": "number",
|
|
1870
|
+
"minimum": 0
|
|
1871
|
+
},
|
|
1872
|
+
"scaleIntervalSec": {
|
|
1873
|
+
"type": "number",
|
|
1874
|
+
"minimum": 0
|
|
1875
|
+
},
|
|
1876
|
+
"applications": {
|
|
1877
|
+
"type": "object",
|
|
1878
|
+
"additionalProperties": {
|
|
1879
|
+
"type": "object",
|
|
1880
|
+
"properties": {
|
|
1881
|
+
"minWorkers": {
|
|
1882
|
+
"type": "number",
|
|
1883
|
+
"minimum": 1
|
|
1884
|
+
},
|
|
1885
|
+
"maxWorkers": {
|
|
1886
|
+
"type": "number",
|
|
1887
|
+
"minimum": 1
|
|
1888
|
+
}
|
|
1889
|
+
},
|
|
1890
|
+
"additionalProperties": false
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
},
|
|
1894
|
+
"additionalProperties": false
|
|
1895
|
+
},
|
|
1831
1896
|
"inspectorOptions": {
|
|
1832
1897
|
"type": "object",
|
|
1833
1898
|
"properties": {
|