@platformatic/runtime 3.18.0 → 3.20.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
@@ -39,6 +39,7 @@ export type PlatformaticRuntimeConfig = {
39
39
  maxHeapUsed?: number | string;
40
40
  maxHeapTotal?: number | string;
41
41
  maxYoungGeneration?: number | string;
42
+ codeRangeSize?: number | string;
42
43
  };
43
44
  dependencies?: string[];
44
45
  arguments?: string[];
@@ -200,6 +201,7 @@ export type PlatformaticRuntimeConfig = {
200
201
  maxHeapUsed?: number | string;
201
202
  maxHeapTotal?: number | string;
202
203
  maxYoungGeneration?: number | string;
204
+ codeRangeSize?: number | string;
203
205
  };
204
206
  undici?: {
205
207
  agentOptions?: {
@@ -306,6 +308,7 @@ export type PlatformaticRuntimeConfig = {
306
308
  body?: string;
307
309
  };
308
310
  };
311
+ healthChecksTimeouts?: number | string;
309
312
  plugins?: string[];
310
313
  timeout?: number | string;
311
314
  /**
package/index.d.ts CHANGED
@@ -57,7 +57,7 @@ export module symbols {
57
57
  export declare const kHealthCheckTimer: unique symbol
58
58
  export declare const kHealthMetricsTimer: unique symbol
59
59
  export declare const kLastHealthCheckELU: unique symbol
60
- export declare const kLastVerticalScalerELU: unique symbol
60
+ export declare const kLastWorkerScalerELU: unique symbol
61
61
  export declare const kWorkerStatus: unique symbol
62
62
  export declare const kWorkerHealthSignals: unique symbol
63
63
  export declare const kStderrMarker: string
package/index.js CHANGED
@@ -63,7 +63,10 @@ export async function loadConfiguration (configOrRoot, sourceOrConfig, context)
63
63
  const { root, source } = await resolve(configOrRoot, sourceOrConfig, 'runtime')
64
64
 
65
65
  // First of all, load the configuration without any validation
66
- const config = await utilsLoadConfiguration(source)
66
+ const config = await utilsLoadConfiguration(source, null, {
67
+ root,
68
+ envFile: context?.envFile
69
+ })
67
70
  const mod = extractModuleFromSchemaUrl(config)
68
71
  if (mod?.module !== '@platformatic/runtime') {
69
72
  return wrapInRuntimeConfig(config, context)
@@ -68,7 +68,7 @@ async function checkLiveness (runtime) {
68
68
  return check
69
69
  } else if (typeof check === 'object') {
70
70
  response = check
71
- return check.status
71
+ return check.status || false
72
72
  }
73
73
  return false
74
74
  })
@@ -85,7 +85,7 @@ export async function startPrometheusServer (runtime, opts) {
85
85
  const metricsEndpoint = opts.endpoint ?? DEFAULT_METRICS_ENDPOINT
86
86
  const auth = opts.auth ?? null
87
87
 
88
- const promServer = fastify({ name: 'Prometheus server' })
88
+ const promServer = fastify({ name: 'Prometheus server', loggerInstance: runtime.logger })
89
89
  promServer.register(fastifyAccepts)
90
90
 
91
91
  let onRequestHook
@@ -151,7 +151,7 @@ export async function startPrometheusServer (runtime, opts) {
151
151
  url: readinessEndpoint,
152
152
  method: 'GET',
153
153
  logLevel: 'warn',
154
- handler: async (req, reply) => {
154
+ handler: async (_req, reply) => {
155
155
  reply.type('text/plain')
156
156
 
157
157
  const { status, response } = await checkReadiness(runtime)
@@ -190,7 +190,7 @@ export async function startPrometheusServer (runtime, opts) {
190
190
  url: livenessEndpoint,
191
191
  method: 'GET',
192
192
  logLevel: 'warn',
193
- handler: async (req, reply) => {
193
+ handler: async (_req, reply) => {
194
194
  reply.type('text/plain')
195
195
 
196
196
  const { status, response, readiness } = await checkLiveness(runtime)
package/lib/runtime.js CHANGED
@@ -24,7 +24,6 @@ import SonicBoom from 'sonic-boom'
24
24
  import { Agent, request, interceptors as undiciInterceptors } from 'undici'
25
25
  import { createThreadInterceptor } from 'undici-thread-interceptor'
26
26
  import { pprofCapturePreloadPath } from './config.js'
27
- import { DynamicWorkersScaler } from './dynamic-workers-scaler.js'
28
27
  import {
29
28
  ApplicationAlreadyStartedError,
30
29
  ApplicationNotFoundError,
@@ -45,8 +44,9 @@ import { startPrometheusServer } from './prom-server.js'
45
44
  import { startScheduler } from './scheduler.js'
46
45
  import { createSharedStore } from './shared-http-cache.js'
47
46
  import { version } from './version.js'
47
+ import { DynamicWorkersScaler } from './worker-scaler.js'
48
48
  import { HealthSignalsQueue } from './worker/health-signals.js'
49
- import { sendViaITC, waitEventFromITC } from './worker/itc.js'
49
+ import { sendMultipleViaITC, sendViaITC, waitEventFromITC } from './worker/itc.js'
50
50
  import { RoundRobinMap } from './worker/round-robin-map.js'
51
51
  import {
52
52
  kApplicationId,
@@ -206,7 +206,7 @@ export class Runtime extends EventEmitter {
206
206
  if (this.#config.workers.dynamic) {
207
207
  if (this.#config.workers.dynamic === false) {
208
208
  this.logger.warn(
209
- `Vertical scaler disabled because the "workers" configuration is set to ${this.#config.workers.static}.`
209
+ `Worker scaler disabled because the "workers" configuration is set to ${this.#config.workers.static}.`
210
210
  )
211
211
  } else {
212
212
  this.#dynamicWorkersScaler = new DynamicWorkersScaler(this, this.#config.workers)
@@ -698,7 +698,9 @@ export class Runtime extends EventEmitter {
698
698
 
699
699
  // TODO: Remove in next major version
700
700
  startCollectingMetrics () {
701
- this.logger.warn('startCollectingMetrics() is deprecated and no longer collects metrics. Metrics are now polled on-demand by the management API.')
701
+ this.logger.warn(
702
+ 'startCollectingMetrics() is deprecated and no longer collects metrics. Metrics are now polled on-demand by the management API.'
703
+ )
702
704
  }
703
705
 
704
706
  // TODO: Remove in next major version
@@ -945,31 +947,45 @@ export class Runtime extends EventEmitter {
945
947
  }
946
948
 
947
949
  async getCustomHealthChecks () {
948
- const status = {}
950
+ const invocations = []
949
951
 
950
952
  for (const id of this.#applications.keys()) {
951
953
  const workersIds = this.#workers.getKeys(id)
952
954
  for (const workerId of workersIds) {
953
- const worker = this.#workers.get(workerId)
954
- status[workerId] = await sendViaITC(worker, 'getCustomHealthCheck')
955
+ invocations.push([workerId, this.#workers.get(workerId)])
955
956
  }
956
957
  }
957
958
 
958
- return status
959
+ return sendMultipleViaITC(
960
+ invocations,
961
+ 'getCustomHealthCheck',
962
+ undefined,
963
+ [],
964
+ this.#concurrency,
965
+ this.#config.metrics.healthChecksTimeout,
966
+ {}
967
+ )
959
968
  }
960
969
 
961
970
  async getCustomReadinessChecks () {
962
- const status = {}
971
+ const invocations = []
963
972
 
964
973
  for (const id of this.#applications.keys()) {
965
974
  const workersIds = this.#workers.getKeys(id)
966
975
  for (const workerId of workersIds) {
967
- const worker = this.#workers.get(workerId)
968
- status[workerId] = await sendViaITC(worker, 'getCustomReadinessCheck')
976
+ invocations.push([workerId, this.#workers.get(workerId)])
969
977
  }
970
978
  }
971
979
 
972
- return status
980
+ return sendMultipleViaITC(
981
+ invocations,
982
+ 'getCustomReadinessCheck',
983
+ undefined,
984
+ [],
985
+ this.#concurrency,
986
+ this.#config.metrics.healthChecksTimeout,
987
+ {}
988
+ )
973
989
  }
974
990
 
975
991
  async getMetrics (format = 'json') {
@@ -1435,26 +1451,20 @@ export class Runtime extends EventEmitter {
1435
1451
  workerEnv.NODE_OPTIONS = `${originalNodeOptions} ${applicationConfig.nodeOptions}`.trim()
1436
1452
  }
1437
1453
 
1438
- let resourceLimits
1439
-
1440
- {
1441
- const maxHeapTotal =
1442
- typeof health.maxHeapTotal === 'string' ? parseMemorySize(health.maxHeapTotal) : health.maxHeapTotal
1443
- const maxYoungGeneration =
1444
- typeof health.maxYoungGeneration === 'string'
1445
- ? parseMemorySize(health.maxYoungGeneration)
1446
- : health.maxYoungGeneration
1454
+ const maxHeapTotal =
1455
+ typeof health.maxHeapTotal === 'string' ? parseMemorySize(health.maxHeapTotal) : health.maxHeapTotal
1456
+ const maxYoungGeneration =
1457
+ typeof health.maxYoungGeneration === 'string'
1458
+ ? parseMemorySize(health.maxYoungGeneration)
1459
+ : health.maxYoungGeneration
1460
+ const codeRangeSize =
1461
+ typeof health.codeRangeSize === 'string' ? parseMemorySize(health.codeRangeSize) : health.codeRangeSize
1447
1462
 
1448
- const maxOldGenerationSizeMb = maxHeapTotal ? Math.floor((maxYoungGeneration > 0 ? maxHeapTotal - maxYoungGeneration : maxHeapTotal) / (1024 * 1024)) : undefined
1449
- const maxYoungGenerationSizeMb = maxYoungGeneration ? Math.floor(maxYoungGeneration / (1024 * 1024)) : undefined
1450
-
1451
- if (maxOldGenerationSizeMb || maxYoungGenerationSizeMb) {
1452
- resourceLimits = {
1453
- maxOldGenerationSizeMb,
1454
- maxYoungGenerationSizeMb
1455
- }
1456
- }
1457
- }
1463
+ const maxOldGenerationSizeMb = Math.floor(
1464
+ (maxYoungGeneration > 0 ? maxHeapTotal - maxYoungGeneration : maxHeapTotal) / (1024 * 1024)
1465
+ )
1466
+ const maxYoungGenerationSizeMb = maxYoungGeneration ? Math.floor(maxYoungGeneration / (1024 * 1024)) : undefined
1467
+ const codeRangeSizeMb = codeRangeSize ? Math.floor(codeRangeSize / (1024 * 1024)) : undefined
1458
1468
 
1459
1469
  const worker = new Worker(kWorkerFile, {
1460
1470
  workerData: {
@@ -1478,7 +1488,11 @@ export class Runtime extends EventEmitter {
1478
1488
  argv: applicationConfig.arguments,
1479
1489
  execArgv,
1480
1490
  env: workerEnv,
1481
- resourceLimits,
1491
+ resourceLimits: {
1492
+ maxOldGenerationSizeMb,
1493
+ maxYoungGenerationSizeMb,
1494
+ codeRangeSizeMb
1495
+ },
1482
1496
  stdout: true,
1483
1497
  stderr: true
1484
1498
  })
@@ -1745,7 +1759,7 @@ export class Runtime extends EventEmitter {
1745
1759
  const memoryUsage = health.heapUsed / maxHeapTotal
1746
1760
  const unhealthy = health.elu > maxELU || memoryUsage > maxHeapUsed
1747
1761
 
1748
- this.emitAndNotify('application:worker:health', {
1762
+ this.emit('application:worker:health', {
1749
1763
  id: worker[kId],
1750
1764
  application: id,
1751
1765
  worker: index,
package/lib/worker/itc.js CHANGED
@@ -1,4 +1,4 @@
1
- import { ensureLoggableError } from '@platformatic/foundation'
1
+ import { ensureLoggableError, executeInParallel, executeWithTimeout, kTimeout } from '@platformatic/foundation'
2
2
  import { ITC } from '@platformatic/itc'
3
3
  import { Unpromise } from '@watchable/unpromise'
4
4
  import { once } from 'node:events'
@@ -65,6 +65,29 @@ export async function sendViaITC (worker, name, message, transferList) {
65
65
  return safeHandleInITC(worker, () => worker[kITC].send(name, message, { transferList }))
66
66
  }
67
67
 
68
+ export async function sendMultipleViaITC (
69
+ idsAndWorkerPairs,
70
+ name,
71
+ message,
72
+ transferList,
73
+ concurrency,
74
+ timeout = 5000,
75
+ timeoutFallbackValue = kTimeout
76
+ ) {
77
+ const results = await executeInParallel(
78
+ async (id, worker) => {
79
+ return [
80
+ id,
81
+ await executeWithTimeout(sendViaITC(worker, name, message, transferList), timeout, timeoutFallbackValue)
82
+ ]
83
+ },
84
+ idsAndWorkerPairs,
85
+ concurrency
86
+ )
87
+
88
+ return Object.fromEntries(results)
89
+ }
90
+
68
91
  export async function waitEventFromITC (worker, event) {
69
92
  return safeHandleInITC(worker, () => once(worker[kITC], event))
70
93
  }
@@ -10,7 +10,7 @@ export const kWorkerHealthSignals = Symbol.for('plt.runtime.worker.healthSignals
10
10
  export const kWorkerStartTime = Symbol.for('plt.runtime.worker.startTime')
11
11
  export const kInterceptors = Symbol.for('plt.runtime.worker.interceptors')
12
12
  export const kLastHealthCheckELU = Symbol.for('plt.runtime.worker.lastHealthCheckELU')
13
- export const kLastVerticalScalerELU = Symbol.for('plt.runtime.worker.lastVerticalScalerELU')
13
+ export const kLastWorkerScalerELU = Symbol.for('plt.runtime.worker.lastWorkerScalerELU')
14
14
 
15
15
  // This string marker should be safe to use since it belongs to Unicode private area
16
16
  export const kStderrMarker = '\ue002'
@@ -2,7 +2,7 @@ import { features } from '@platformatic/foundation'
2
2
  import { availableParallelism } from 'node:os'
3
3
  import { getMemoryInfo } from './metrics.js'
4
4
  import { ScalingAlgorithm, scaleUpELUThreshold } from './scaling-algorithm.js'
5
- import { kApplicationId, kId, kLastVerticalScalerELU, kWorkerStartTime, kWorkerStatus } from './worker/symbols.js'
5
+ import { kApplicationId, kId, kLastWorkerScalerELU, kWorkerStartTime, kWorkerStatus } from './worker/symbols.js'
6
6
 
7
7
  const healthCheckInterval = 1000
8
8
  export const kOriginalWorkers = Symbol('plt.runtime.application.dynamicWorkersScalerOriginalWorkers')
@@ -135,13 +135,13 @@ export class DynamicWorkersScaler {
135
135
  }
136
136
 
137
137
  try {
138
- const health = await this.#runtime.getWorkerHealth(worker, { previousELU: worker[kLastVerticalScalerELU] })
138
+ const health = await this.#runtime.getWorkerHealth(worker, { previousELU: worker[kLastWorkerScalerELU] })
139
139
 
140
140
  if (!health) {
141
141
  continue
142
142
  }
143
143
 
144
- worker[kLastVerticalScalerELU] = health.currentELU
144
+ worker[kLastWorkerScalerELU] = health.currentELU
145
145
 
146
146
  this.#algorithm.addWorkerHealthInfo({
147
147
  workerId: worker[kId],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/runtime",
3
- "version": "3.18.0",
3
+ "version": "3.20.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -35,14 +35,14 @@
35
35
  "typescript": "^5.5.4",
36
36
  "undici-oidc-interceptor": "^0.5.0",
37
37
  "why-is-node-running": "^2.2.2",
38
- "@platformatic/composer": "3.18.0",
39
- "@platformatic/node": "3.18.0",
40
- "@platformatic/gateway": "3.18.0",
41
- "@platformatic/db": "3.18.0",
42
- "@platformatic/sql-graphql": "3.18.0",
43
- "@platformatic/sql-mapper": "3.18.0",
44
- "@platformatic/wattpm-pprof-capture": "3.18.0",
45
- "@platformatic/service": "3.18.0"
38
+ "@platformatic/composer": "3.20.0",
39
+ "@platformatic/db": "3.20.0",
40
+ "@platformatic/gateway": "3.20.0",
41
+ "@platformatic/service": "3.20.0",
42
+ "@platformatic/sql-graphql": "3.20.0",
43
+ "@platformatic/sql-mapper": "3.20.0",
44
+ "@platformatic/node": "3.20.0",
45
+ "@platformatic/wattpm-pprof-capture": "3.20.0"
46
46
  },
47
47
  "dependencies": {
48
48
  "@fastify/accepts": "^5.0.0",
@@ -71,12 +71,12 @@
71
71
  "undici": "^7.0.0",
72
72
  "undici-thread-interceptor": "^0.15.0",
73
73
  "ws": "^8.16.0",
74
- "@platformatic/basic": "3.18.0",
75
- "@platformatic/generators": "3.18.0",
76
- "@platformatic/foundation": "3.18.0",
77
- "@platformatic/itc": "3.18.0",
78
- "@platformatic/metrics": "3.18.0",
79
- "@platformatic/telemetry": "3.18.0"
74
+ "@platformatic/itc": "3.20.0",
75
+ "@platformatic/foundation": "3.20.0",
76
+ "@platformatic/generators": "3.20.0",
77
+ "@platformatic/metrics": "3.20.0",
78
+ "@platformatic/basic": "3.20.0",
79
+ "@platformatic/telemetry": "3.20.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.18.0.json",
2
+ "$id": "https://schemas.platformatic.dev/@platformatic/runtime/3.20.0.json",
3
3
  "$schema": "http://json-schema.org/draft-07/schema#",
4
4
  "title": "Platformatic Runtime Config",
5
5
  "type": "object",
@@ -187,6 +187,17 @@
187
187
  "type": "string"
188
188
  }
189
189
  ]
190
+ },
191
+ "codeRangeSize": {
192
+ "anyOf": [
193
+ {
194
+ "type": "number",
195
+ "minimum": 0
196
+ },
197
+ {
198
+ "type": "string"
199
+ }
200
+ ]
190
201
  }
191
202
  },
192
203
  "additionalProperties": false
@@ -469,6 +480,17 @@
469
480
  "type": "string"
470
481
  }
471
482
  ]
483
+ },
484
+ "codeRangeSize": {
485
+ "anyOf": [
486
+ {
487
+ "type": "number",
488
+ "minimum": 0
489
+ },
490
+ {
491
+ "type": "string"
492
+ }
493
+ ]
472
494
  }
473
495
  },
474
496
  "additionalProperties": false
@@ -749,6 +771,17 @@
749
771
  "type": "string"
750
772
  }
751
773
  ]
774
+ },
775
+ "codeRangeSize": {
776
+ "anyOf": [
777
+ {
778
+ "type": "number",
779
+ "minimum": 0
780
+ },
781
+ {
782
+ "type": "string"
783
+ }
784
+ ]
752
785
  }
753
786
  },
754
787
  "additionalProperties": false
@@ -1029,6 +1062,17 @@
1029
1062
  "type": "string"
1030
1063
  }
1031
1064
  ]
1065
+ },
1066
+ "codeRangeSize": {
1067
+ "anyOf": [
1068
+ {
1069
+ "type": "number",
1070
+ "minimum": 0
1071
+ },
1072
+ {
1073
+ "type": "string"
1074
+ }
1075
+ ]
1032
1076
  }
1033
1077
  },
1034
1078
  "additionalProperties": false
@@ -1626,7 +1670,8 @@
1626
1670
  {
1627
1671
  "type": "string"
1628
1672
  }
1629
- ]
1673
+ ],
1674
+ "default": 4294967296
1630
1675
  },
1631
1676
  "maxYoungGeneration": {
1632
1677
  "anyOf": [
@@ -1637,7 +1682,20 @@
1637
1682
  {
1638
1683
  "type": "string"
1639
1684
  }
1640
- ]
1685
+ ],
1686
+ "default": 134217728
1687
+ },
1688
+ "codeRangeSize": {
1689
+ "anyOf": [
1690
+ {
1691
+ "type": "number",
1692
+ "minimum": 0
1693
+ },
1694
+ {
1695
+ "type": "string"
1696
+ }
1697
+ ],
1698
+ "default": 268435456
1641
1699
  }
1642
1700
  },
1643
1701
  "additionalProperties": false
@@ -1953,6 +2011,17 @@
1953
2011
  }
1954
2012
  ]
1955
2013
  },
2014
+ "healthChecksTimeouts": {
2015
+ "anyOf": [
2016
+ {
2017
+ "type": "integer"
2018
+ },
2019
+ {
2020
+ "type": "string"
2021
+ }
2022
+ ],
2023
+ "default": 5000
2024
+ },
1956
2025
  "plugins": {
1957
2026
  "type": "array",
1958
2027
  "items": {