@platformatic/runtime 3.10.0 → 3.11.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 CHANGED
@@ -365,12 +365,13 @@ export type PlatformaticRuntimeConfig = {
365
365
  verticalScaler?: {
366
366
  enabled?: boolean;
367
367
  maxTotalWorkers?: number;
368
+ maxTotalMemory?: number;
368
369
  minWorkers?: number;
369
370
  maxWorkers?: number;
370
371
  scaleUpELU?: number;
371
372
  scaleDownELU?: number;
372
- minELUDiff?: number;
373
373
  timeWindowSec?: number;
374
+ scaleDownTimeWindowSec?: number;
374
375
  cooldownSec?: number;
375
376
  scaleIntervalSec?: number;
376
377
  gracePeriod?: number;
package/lib/metrics.js ADDED
@@ -0,0 +1,73 @@
1
+ import { readFile } from 'node:fs/promises'
2
+ import si from 'systeminformation'
3
+
4
+ async function readNumberFromCgroupFile (path) {
5
+ try {
6
+ const raw = (await readFile(path, 'utf8')).trim()
7
+ if (raw === 'max') return null
8
+ return Number(raw)
9
+ } catch {
10
+ return null
11
+ }
12
+ }
13
+
14
+ async function getCgroupV2MemoryInfo () {
15
+ let [total, used] = await Promise.all([
16
+ readNumberFromCgroupFile('/sys/fs/cgroup/memory.max'),
17
+ readNumberFromCgroupFile('/sys/fs/cgroup/memory.current')
18
+ ])
19
+ if (total == null && used == null) return null
20
+
21
+ if (total === null) {
22
+ const mem = await si.mem()
23
+ total = mem.total
24
+ }
25
+
26
+ return { scope: 'cgroup-v2', used, total }
27
+ }
28
+
29
+ async function getCgroupV1MemoryInfo () {
30
+ let [total, used] = await Promise.all([
31
+ readNumberFromCgroupFile('/sys/fs/cgroup/memory/memory.limit_in_bytes'),
32
+ readNumberFromCgroupFile('/sys/fs/cgroup/memory/memory.usage_in_bytes')
33
+ ])
34
+ if (total == null && used == null) return null
35
+
36
+ // Some v1 setups report 9.22e18 (≈unlimited)
37
+ if (total === null || total > 1e18) {
38
+ const mem = await si.mem()
39
+ total = mem.total
40
+ }
41
+
42
+ return { scope: 'cgroup-v1', used, total }
43
+ }
44
+
45
+ async function readHostMemoryInfo () {
46
+ const mem = await si.mem()
47
+ return { scope: 'host', used: mem.active, total: mem.total }
48
+ }
49
+
50
+ export async function getMemoryInfo (options = {}) {
51
+ const scope = options.scope
52
+
53
+ if (scope === 'cgroup-v2') {
54
+ return getCgroupV2MemoryInfo()
55
+ }
56
+ if (scope === 'cgroup-v1') {
57
+ return getCgroupV1MemoryInfo()
58
+ }
59
+ if (scope === 'host') {
60
+ return readHostMemoryInfo()
61
+ }
62
+
63
+ let memInfo = await getCgroupV2MemoryInfo()
64
+
65
+ if (!memInfo) {
66
+ memInfo = await getCgroupV1MemoryInfo()
67
+ }
68
+ if (!memInfo) {
69
+ memInfo = await readHostMemoryInfo()
70
+ }
71
+
72
+ return memInfo
73
+ }
package/lib/runtime.js CHANGED
@@ -47,6 +47,7 @@ import { createSharedStore } from './shared-http-cache.js'
47
47
  import { version } from './version.js'
48
48
  import { sendViaITC, waitEventFromITC } from './worker/itc.js'
49
49
  import { RoundRobinMap } from './worker/round-robin-map.js'
50
+ import { getMemoryInfo } from './metrics.js'
50
51
  import {
51
52
  kApplicationId,
52
53
  kConfig,
@@ -2473,28 +2474,32 @@ export class Runtime extends EventEmitter {
2473
2474
  }
2474
2475
 
2475
2476
  const scalerConfig = this.#config.verticalScaler
2477
+ const memInfo = await getMemoryInfo()
2478
+ const memScope = memInfo.scope
2476
2479
 
2477
2480
  scalerConfig.maxTotalWorkers ??= os.availableParallelism()
2481
+ scalerConfig.maxTotalMemory ??= memInfo.total * 0.9
2478
2482
  scalerConfig.maxWorkers ??= scalerConfig.maxTotalWorkers
2479
2483
  scalerConfig.minWorkers ??= 1
2480
2484
  scalerConfig.cooldownSec ??= 60
2481
2485
  scalerConfig.scaleUpELU ??= 0.8
2482
2486
  scalerConfig.scaleDownELU ??= 0.2
2483
- scalerConfig.minELUDiff ??= 0.2
2484
2487
  scalerConfig.scaleIntervalSec ??= 60
2485
- scalerConfig.timeWindowSec ??= 60
2488
+ scalerConfig.timeWindowSec ??= 10
2489
+ scalerConfig.scaleDownTimeWindowSec ??= 60
2486
2490
  scalerConfig.gracePeriod ??= 30 * 1000
2487
2491
  scalerConfig.applications ??= {}
2488
2492
 
2489
2493
  const maxTotalWorkers = scalerConfig.maxTotalWorkers
2494
+ const maxTotalMemory = scalerConfig.maxTotalMemory
2490
2495
  const maxWorkers = scalerConfig.maxWorkers
2491
2496
  const minWorkers = scalerConfig.minWorkers
2492
2497
  const cooldown = scalerConfig.cooldownSec
2493
2498
  const scaleUpELU = scalerConfig.scaleUpELU
2494
2499
  const scaleDownELU = scalerConfig.scaleDownELU
2495
- const minELUDiff = scalerConfig.minELUDiff
2496
2500
  const scaleIntervalSec = scalerConfig.scaleIntervalSec
2497
2501
  const timeWindowSec = scalerConfig.timeWindowSec
2502
+ const scaleDownTimeWindowSec = scalerConfig.scaleDownTimeWindowSec
2498
2503
  const applicationsConfigs = scalerConfig.applications
2499
2504
  const gracePeriod = scalerConfig.gracePeriod
2500
2505
  const healthCheckInterval = 1000
@@ -2558,8 +2563,8 @@ export class Runtime extends EventEmitter {
2558
2563
  maxTotalWorkers,
2559
2564
  scaleUpELU,
2560
2565
  scaleDownELU,
2561
- minELUDiff,
2562
- timeWindowSec,
2566
+ scaleUpTimeWindowSec: timeWindowSec,
2567
+ scaleDownTimeWindowSec,
2563
2568
  applications: applicationsConfigs
2564
2569
  })
2565
2570
 
@@ -2583,7 +2588,9 @@ export class Runtime extends EventEmitter {
2583
2588
  scalingAlgorithm.addWorkerHealthInfo({
2584
2589
  workerId: worker[kId],
2585
2590
  applicationId: worker[kApplicationId],
2586
- elu: health.elu
2591
+ elu: health.elu,
2592
+ heapUsed: health.heapUsed,
2593
+ heapTotal: health.heapTotal
2587
2594
  })
2588
2595
 
2589
2596
  if (health.elu > scaleUpELU) {
@@ -2611,6 +2618,7 @@ export class Runtime extends EventEmitter {
2611
2618
 
2612
2619
  try {
2613
2620
  const workersInfo = await this.getWorkers()
2621
+ const mem = await getMemoryInfo({ scope: memScope })
2614
2622
 
2615
2623
  const appsWorkersInfo = {}
2616
2624
  for (const worker of Object.values(workersInfo)) {
@@ -2621,7 +2629,10 @@ export class Runtime extends EventEmitter {
2621
2629
  appsWorkersInfo[applicationId]++
2622
2630
  }
2623
2631
 
2624
- const recommendations = scalingAlgorithm.getRecommendations(appsWorkersInfo)
2632
+ const availableMemory = maxTotalMemory - mem.used
2633
+ const recommendations = scalingAlgorithm.getRecommendations(appsWorkersInfo, {
2634
+ availableMemory
2635
+ })
2625
2636
  if (recommendations.length > 0) {
2626
2637
  await applyRecommendations(recommendations)
2627
2638
  lastScaling = Date.now()
@@ -2,60 +2,63 @@ class ScalingAlgorithm {
2
2
  #scaleUpELU
3
3
  #scaleDownELU
4
4
  #maxTotalWorkers
5
- #timeWindowSec
6
- #appsELUs
7
- #minELUDiff
5
+ #scaleUpTimeWindowSec
6
+ #scaleDownTimeWindowSec
7
+ #appsMetrics
8
8
  #appsConfigs
9
9
 
10
10
  constructor (options = {}) {
11
11
  this.#scaleUpELU = options.scaleUpELU ?? 0.8
12
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
13
+ this.#maxTotalWorkers = options.maxTotalWorkers ?? Infinity
14
+ this.#scaleUpTimeWindowSec = options.scaleUpTimeWindowSec ?? 10
15
+ this.#scaleDownTimeWindowSec = options.scaleDownTimeWindowSec ?? 60
16
16
  this.#appsConfigs = options.applications ?? {}
17
17
 
18
- this.#appsELUs = {}
18
+ this.#appsMetrics = {}
19
19
  }
20
20
 
21
21
  addWorkerHealthInfo (healthInfo) {
22
- const { workerId, applicationId, elu } = healthInfo
22
+ const { workerId, applicationId, elu, heapUsed } = healthInfo
23
23
  const timestamp = Date.now()
24
24
 
25
- if (!this.#appsELUs[applicationId]) {
26
- this.#appsELUs[applicationId] = {}
25
+ if (!this.#appsMetrics[applicationId]) {
26
+ this.#appsMetrics[applicationId] = {}
27
27
  }
28
- if (!this.#appsELUs[applicationId][workerId]) {
29
- this.#appsELUs[applicationId][workerId] = []
28
+ if (!this.#appsMetrics[applicationId][workerId]) {
29
+ this.#appsMetrics[applicationId][workerId] = []
30
30
  }
31
- this.#appsELUs[applicationId][workerId].push({ elu, timestamp })
31
+ this.#appsMetrics[applicationId][workerId].push({
32
+ elu,
33
+ timestamp,
34
+ heapUsed
35
+ })
32
36
  this.#removeOutdatedAppELUs(applicationId)
33
37
  }
34
38
 
35
- getRecommendations (appsWorkersInfo) {
39
+ getRecommendations (appsWorkersInfo, options = {}) {
36
40
  let totalWorkersCount = 0
37
- let appsInfo = []
41
+ let totalAvailableMemory = options.availableMemory ?? Infinity
42
+
43
+ const appsInfo = []
38
44
 
39
45
  for (const applicationId in appsWorkersInfo) {
40
46
  const workersCount = appsWorkersInfo[applicationId]
41
- const elu = this.#calculateAppAvgELU(applicationId)
42
- appsInfo.push({ applicationId, workersCount, elu })
47
+
48
+ const { heapUsed } = this.#calculateAppAvgMetrics(applicationId)
49
+
50
+ appsInfo.push({
51
+ applicationId,
52
+ workersCount,
53
+ avgHeapUsed: heapUsed,
54
+ })
55
+
43
56
  totalWorkersCount += workersCount
44
57
  }
45
58
 
46
- appsInfo = appsInfo.sort(
47
- (app1, app2) => {
48
- if (app1.elu > app2.elu) return 1
49
- if (app1.elu < app2.elu) return -1
50
- if (app1.workersCount < app2.workersCount) return 1
51
- if (app1.workersCount > app2.workersCount) return -1
52
- return 0
53
- }
54
- )
55
-
56
59
  const recommendations = []
57
60
 
58
- for (const { applicationId, elu, workersCount } of appsInfo) {
61
+ for (const { applicationId, workersCount, avgHeapUsed } of appsInfo) {
59
62
  const appMinWorkers = this.#appsConfigs[applicationId]?.minWorkers ?? 1
60
63
  const appMaxWorkers = this.#appsConfigs[applicationId]?.maxWorkers ?? this.#maxTotalWorkers
61
64
 
@@ -65,7 +68,10 @@ class ScalingAlgorithm {
65
68
  workersCount: appMinWorkers,
66
69
  direction: 'up'
67
70
  })
68
- totalWorkersCount += appMinWorkers - workersCount
71
+
72
+ const newWorkersCount = appMinWorkers - workersCount
73
+ totalWorkersCount += newWorkersCount
74
+ totalAvailableMemory += newWorkersCount * avgHeapUsed
69
75
  continue
70
76
  }
71
77
 
@@ -75,103 +81,122 @@ class ScalingAlgorithm {
75
81
  workersCount: appMaxWorkers,
76
82
  direction: 'down'
77
83
  })
78
- totalWorkersCount -= workersCount - appMaxWorkers
84
+
85
+ const removedWorkersCount = workersCount - appMaxWorkers
86
+ totalWorkersCount -= removedWorkersCount
87
+ totalAvailableMemory -= removedWorkersCount * avgHeapUsed
79
88
  continue
80
89
  }
81
90
 
82
- if (elu < this.#scaleDownELU && workersCount > appMinWorkers) {
83
- recommendations.push({
84
- applicationId,
85
- workersCount: workersCount - 1,
86
- direction: 'down'
87
- })
88
- totalWorkersCount--
91
+ if (workersCount > appMinWorkers) {
92
+ const recommendation = this.#getApplicationScaleRecommendation(applicationId)
93
+ if (recommendation.recommendation === 'scaleDown') {
94
+ recommendations.push({
95
+ applicationId,
96
+ workersCount: workersCount - 1,
97
+ direction: 'down'
98
+ })
99
+
100
+ const removedWorkersCount = 1
101
+ totalWorkersCount -= removedWorkersCount
102
+ totalAvailableMemory -= removedWorkersCount * avgHeapUsed
103
+ }
89
104
  }
90
105
  }
91
106
 
92
- for (const scaleUpCandidate of appsInfo.toReversed()) {
93
- if (scaleUpCandidate.elu < this.#scaleUpELU) break
94
-
95
- const { applicationId, workersCount } = scaleUpCandidate
96
-
97
- const isScaled = recommendations.some(
98
- r => r.applicationId === applicationId &&
99
- r.direction === 'up'
100
- )
101
- if (isScaled) continue
102
-
103
- const appMaxWorkers = this.#appsConfigs[applicationId]?.maxWorkers ?? this.#maxTotalWorkers
104
- if (workersCount >= appMaxWorkers) continue
105
-
106
- if (totalWorkersCount >= this.#maxTotalWorkers) {
107
- let scaleDownCandidate = null
108
- for (const app of appsInfo) {
109
- const appMinWorkers = this.#appsConfigs[app.applicationId]?.minWorkers ?? 1
110
- if (app.workersCount > appMinWorkers) {
111
- scaleDownCandidate = app
112
- break
107
+ if (totalWorkersCount < this.#maxTotalWorkers) {
108
+ let scaleUpCandidate = null
109
+
110
+ for (const { applicationId, workersCount, avgHeapUsed } of appsInfo) {
111
+ const appMaxWorkers = this.#appsConfigs[applicationId]?.maxWorkers ?? this.#maxTotalWorkers
112
+ if (workersCount >= appMaxWorkers) continue
113
+ if (avgHeapUsed >= totalAvailableMemory) continue
114
+
115
+ const isScaled = recommendations.some(
116
+ r => r.applicationId === applicationId
117
+ )
118
+ if (isScaled) continue
119
+
120
+ const recommendation = this.#getApplicationScaleRecommendation(applicationId)
121
+ if (recommendation.recommendation !== 'scaleUp') continue
122
+
123
+ if (
124
+ !scaleUpCandidate ||
125
+ (recommendation.scaleUpELU > scaleUpCandidate.scaleUpELU) ||
126
+ (recommendation.scaleUpELU === scaleUpCandidate.scaleUpELU &&
127
+ workersCount < scaleUpCandidate.workersCount
128
+ )
129
+ ) {
130
+ scaleUpCandidate = {
131
+ applicationId,
132
+ workersCount,
133
+ heapUsed: recommendation.avgHeapUsage,
134
+ elu: recommendation.scaleUpELU
113
135
  }
114
136
  }
137
+ }
115
138
 
116
- if (scaleDownCandidate) {
117
- const eluDiff = scaleUpCandidate.elu - scaleDownCandidate.elu
118
- const workersDiff = scaleDownCandidate.workersCount - scaleUpCandidate.workersCount
119
-
120
- if (eluDiff >= this.#minELUDiff || workersDiff >= 2) {
121
- recommendations.push({
122
- applicationId: scaleDownCandidate.applicationId,
123
- workersCount: scaleDownCandidate.workersCount - 1,
124
- direction: 'down'
125
- })
126
- recommendations.push({
127
- applicationId,
128
- workersCount: workersCount + 1,
129
- direction: 'up'
130
- })
131
- }
132
- }
133
- } else {
139
+ if (scaleUpCandidate) {
134
140
  recommendations.push({
135
- applicationId,
136
- workersCount: workersCount + 1,
141
+ applicationId: scaleUpCandidate.applicationId,
142
+ workersCount: scaleUpCandidate.workersCount + 1,
137
143
  direction: 'up'
138
144
  })
139
145
  totalWorkersCount++
146
+ totalAvailableMemory -= scaleUpCandidate.heapUsed
140
147
  }
141
- break
142
148
  }
143
149
 
144
150
  return recommendations
145
151
  }
146
152
 
147
- #calculateAppAvgELU (applicationId) {
153
+ #calculateAppAvgMetrics (applicationId, options = {}) {
148
154
  this.#removeOutdatedAppELUs(applicationId)
149
155
 
150
- const appELUs = this.#appsELUs[applicationId]
151
- if (!appELUs) return 0
156
+ const appMetrics = this.#appsMetrics[applicationId]
157
+ if (!appMetrics) return { elu: 0, heapUsed: 0 }
158
+
159
+ const defaultTimeWindow = this.#getMetricsTimeWindow()
160
+ const timeWindow = options.timeWindow ?? defaultTimeWindow
152
161
 
153
162
  let eluSum = 0
154
- let eluCount = 0
163
+ let heapUsedSum = 0
164
+ let count = 0
155
165
 
156
- for (const workerId in appELUs) {
157
- const workerELUs = appELUs[workerId]
158
- const workerELUSum = workerELUs.reduce(
159
- (sum, workerELU) => sum + workerELU.elu, 0
160
- )
161
- eluSum += workerELUSum / workerELUs.length
162
- eluCount++
163
- }
166
+ const now = Date.now()
167
+
168
+ for (const workerId in appMetrics) {
169
+ const workerMetrics = appMetrics[workerId]
170
+
171
+ let workerELUSum = 0
172
+ let workerHeapUsedSum = 0
173
+ let metricCount = 0
174
+
175
+ for (const metric of workerMetrics) {
176
+ if (metric.timestamp < now - timeWindow) continue
177
+ workerELUSum += metric.elu
178
+ workerHeapUsedSum += metric.heapUsed
179
+ metricCount++
180
+ }
164
181
 
165
- if (eluCount === 0) return 0
182
+ if (metricCount === 0) continue
166
183
 
167
- return Math.round(eluSum / eluCount * 100) / 100
184
+ eluSum += workerELUSum / metricCount
185
+ heapUsedSum += workerHeapUsedSum / metricCount
186
+ count++
187
+ }
188
+
189
+ const elu = Math.round(eluSum / count * 100) / 100
190
+ const heapUsed = Math.round(heapUsedSum / count * 100) / 100
191
+ return { elu, heapUsed }
168
192
  }
169
193
 
170
194
  #removeOutdatedAppELUs (applicationId) {
171
- const appELUs = this.#appsELUs[applicationId]
195
+ const appELUs = this.#appsMetrics[applicationId]
172
196
  if (!appELUs) return
173
197
 
174
198
  const now = Date.now()
199
+ const timeWindow = this.#getMetricsTimeWindow()
175
200
 
176
201
  for (const workerId in appELUs) {
177
202
  const workerELUs = appELUs[workerId]
@@ -179,7 +204,7 @@ class ScalingAlgorithm {
179
204
  let firstValidIndex = -1
180
205
  for (let i = 0; i < workerELUs.length; i++) {
181
206
  const timestamp = workerELUs[i].timestamp
182
- if (timestamp >= now - this.#timeWindowSec * 1000) {
207
+ if (timestamp >= now - timeWindow) {
183
208
  firstValidIndex = i
184
209
  break
185
210
  }
@@ -199,6 +224,30 @@ class ScalingAlgorithm {
199
224
  }
200
225
  }
201
226
  }
227
+
228
+ #getMetricsTimeWindow () {
229
+ return Math.max(this.#scaleUpTimeWindowSec, this.#scaleDownTimeWindowSec) * 1000
230
+ }
231
+
232
+ #getApplicationScaleRecommendation (applicationId) {
233
+ const { elu: scaleUpELU } = this.#calculateAppAvgMetrics(applicationId, {
234
+ timeWindow: this.#scaleUpTimeWindowSec * 1000
235
+ })
236
+ const { elu: scaleDownELU } = this.#calculateAppAvgMetrics(applicationId, {
237
+ timeWindow: this.#scaleDownTimeWindowSec * 1000
238
+ })
239
+ const { heapUsed: avgHeapUsage } = this.#calculateAppAvgMetrics(applicationId)
240
+
241
+ let recommendation = null
242
+ if (scaleUpELU > this.#scaleUpELU) {
243
+ recommendation = 'scaleUp'
244
+ }
245
+ if (scaleDownELU < this.#scaleDownELU) {
246
+ recommendation = 'scaleDown'
247
+ }
248
+
249
+ return { recommendation, scaleUpELU, scaleDownELU, avgHeapUsage }
250
+ }
202
251
  }
203
252
 
204
253
  export default ScalingAlgorithm
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/runtime",
3
- "version": "3.10.0",
3
+ "version": "3.11.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -18,8 +18,8 @@
18
18
  "@fastify/compress": "^8.0.0",
19
19
  "@fastify/express": "^4.0.0",
20
20
  "@fastify/formbody": "^8.0.0",
21
- "autocannon": "^8.0.0",
22
21
  "atomic-sleep": "^1.0.0",
22
+ "autocannon": "^8.0.0",
23
23
  "c8": "^10.0.0",
24
24
  "cleaner-spec-reporter": "^0.5.0",
25
25
  "eslint": "9",
@@ -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/composer": "3.10.0",
39
- "@platformatic/db": "3.10.0",
40
- "@platformatic/node": "3.10.0",
41
- "@platformatic/gateway": "3.10.0",
42
- "@platformatic/service": "3.10.0",
43
- "@platformatic/sql-graphql": "3.10.0",
44
- "@platformatic/sql-mapper": "3.10.0",
45
- "@platformatic/wattpm-pprof-capture": "3.10.0"
38
+ "@platformatic/composer": "3.11.0",
39
+ "@platformatic/db": "3.11.0",
40
+ "@platformatic/gateway": "3.11.0",
41
+ "@platformatic/node": "3.11.0",
42
+ "@platformatic/service": "3.11.0",
43
+ "@platformatic/sql-mapper": "3.11.0",
44
+ "@platformatic/wattpm-pprof-capture": "3.11.0",
45
+ "@platformatic/sql-graphql": "3.11.0"
46
46
  },
47
47
  "dependencies": {
48
48
  "@fastify/accepts": "^5.0.0",
@@ -69,15 +69,16 @@
69
69
  "prom-client": "^15.1.2",
70
70
  "semgrator": "^0.3.0",
71
71
  "sonic-boom": "^4.2.0",
72
+ "systeminformation": "^5.27.11",
72
73
  "undici": "^7.0.0",
73
74
  "undici-thread-interceptor": "^0.14.0",
74
75
  "ws": "^8.16.0",
75
- "@platformatic/basic": "3.10.0",
76
- "@platformatic/foundation": "3.10.0",
77
- "@platformatic/itc": "3.10.0",
78
- "@platformatic/generators": "3.10.0",
79
- "@platformatic/metrics": "3.10.0",
80
- "@platformatic/telemetry": "3.10.0"
76
+ "@platformatic/basic": "3.11.0",
77
+ "@platformatic/foundation": "3.11.0",
78
+ "@platformatic/itc": "3.11.0",
79
+ "@platformatic/generators": "3.11.0",
80
+ "@platformatic/metrics": "3.11.0",
81
+ "@platformatic/telemetry": "3.11.0"
81
82
  },
82
83
  "engines": {
83
84
  "node": ">=22.19.0"
package/schema.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://schemas.platformatic.dev/@platformatic/runtime/3.10.0.json",
2
+ "$id": "https://schemas.platformatic.dev/@platformatic/runtime/3.11.0.json",
3
3
  "$schema": "http://json-schema.org/draft-07/schema#",
4
4
  "title": "Platformatic Runtime Config",
5
5
  "type": "object",
@@ -2001,6 +2001,10 @@
2001
2001
  "type": "number",
2002
2002
  "minimum": 1
2003
2003
  },
2004
+ "maxTotalMemory": {
2005
+ "type": "number",
2006
+ "minimum": 0
2007
+ },
2004
2008
  "minWorkers": {
2005
2009
  "type": "number",
2006
2010
  "minimum": 1
@@ -2019,12 +2023,11 @@
2019
2023
  "minimum": 0,
2020
2024
  "maximum": 1
2021
2025
  },
2022
- "minELUDiff": {
2026
+ "timeWindowSec": {
2023
2027
  "type": "number",
2024
- "minimum": 0,
2025
- "maximum": 1
2028
+ "minimum": 0
2026
2029
  },
2027
- "timeWindowSec": {
2030
+ "scaleDownTimeWindowSec": {
2028
2031
  "type": "number",
2029
2032
  "minimum": 0
2030
2033
  },