@platformatic/runtime 2.26.0-alpha.1 → 2.26.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config.d.ts +1 -1
- package/lib/management-api.js +15 -0
- package/lib/runtime.js +91 -57
- package/lib/worker/http-cache.js +35 -9
- package/lib/worker/main.js +12 -1
- package/package.json +15 -14
- package/schema.json +1 -1
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 HttpsSchemasPlatformaticDevPlatformaticRuntime2261Json = {
|
|
9
9
|
[k: string]: unknown;
|
|
10
10
|
} & {
|
|
11
11
|
$schema?: string;
|
package/lib/management-api.js
CHANGED
|
@@ -13,6 +13,8 @@ const { getRuntimeLogsDir } = require('./utils')
|
|
|
13
13
|
const PLATFORMATIC_TMP_DIR = join(tmpdir(), 'platformatic', 'runtimes')
|
|
14
14
|
|
|
15
15
|
async function managementApiPlugin (app, opts) {
|
|
16
|
+
app.register(require('@fastify/accepts'))
|
|
17
|
+
|
|
16
18
|
const runtime = opts.runtime
|
|
17
19
|
|
|
18
20
|
app.get('/status', async () => {
|
|
@@ -113,6 +115,19 @@ async function managementApiPlugin (app, opts) {
|
|
|
113
115
|
reply.code(res.statusCode).headers(res.headers).send(res.body)
|
|
114
116
|
})
|
|
115
117
|
|
|
118
|
+
app.get('/metrics', { logLevel: 'debug' }, async (req, reply) => {
|
|
119
|
+
const accepts = req.accepts()
|
|
120
|
+
|
|
121
|
+
if (!accepts.type('text/plain') && accepts.type('application/json')) {
|
|
122
|
+
const { metrics } = await runtime.getMetrics('json')
|
|
123
|
+
return metrics
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
reply.type('text/plain')
|
|
127
|
+
const { metrics } = await runtime.getMetrics('text')
|
|
128
|
+
return metrics
|
|
129
|
+
})
|
|
130
|
+
|
|
116
131
|
app.get('/metrics/live', { websocket: true }, async socket => {
|
|
117
132
|
const cachedMetrics = runtime.getCachedMetrics()
|
|
118
133
|
if (cachedMetrics.length > 0) {
|
package/lib/runtime.js
CHANGED
|
@@ -687,7 +687,10 @@ class Runtime extends EventEmitter {
|
|
|
687
687
|
}
|
|
688
688
|
} catch (e) {
|
|
689
689
|
// The service exited while we were sending the ITC, skip it
|
|
690
|
-
if (
|
|
690
|
+
if (
|
|
691
|
+
e.code === 'PLT_RUNTIME_SERVICE_NOT_STARTED' ||
|
|
692
|
+
e.code === 'PLT_RUNTIME_SERVICE_EXIT'
|
|
693
|
+
) {
|
|
691
694
|
continue
|
|
692
695
|
}
|
|
693
696
|
|
|
@@ -706,73 +709,104 @@ class Runtime extends EventEmitter {
|
|
|
706
709
|
try {
|
|
707
710
|
const { metrics } = await this.getMetrics()
|
|
708
711
|
|
|
709
|
-
if (metrics === null) {
|
|
712
|
+
if (metrics === null || metrics.length === 0) {
|
|
710
713
|
return null
|
|
711
714
|
}
|
|
712
715
|
|
|
713
|
-
const
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
const
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
if (
|
|
735
|
-
|
|
736
|
-
p90Value = entrypointMetrics.values.find(value => value.labels.quantile === 0.9)?.value || 0
|
|
737
|
-
p95Value = entrypointMetrics.values.find(value => value.labels.quantile === 0.95)?.value || 0
|
|
738
|
-
p99Value = entrypointMetrics.values.find(value => value.labels.quantile === 0.99)?.value || 0
|
|
739
|
-
|
|
740
|
-
p50Value = Math.round(p50Value * 1000)
|
|
741
|
-
p90Value = Math.round(p90Value * 1000)
|
|
742
|
-
p95Value = Math.round(p95Value * 1000)
|
|
743
|
-
p99Value = Math.round(p99Value * 1000)
|
|
716
|
+
const metricsNames = [
|
|
717
|
+
'process_cpu_percent_usage',
|
|
718
|
+
'process_resident_memory_bytes',
|
|
719
|
+
'nodejs_heap_size_total_bytes',
|
|
720
|
+
'nodejs_heap_size_used_bytes',
|
|
721
|
+
'nodejs_heap_space_size_total_bytes',
|
|
722
|
+
'nodejs_eventloop_utilization',
|
|
723
|
+
'http_request_all_summary_seconds'
|
|
724
|
+
]
|
|
725
|
+
|
|
726
|
+
const servicesMetrics = {}
|
|
727
|
+
|
|
728
|
+
for (const metric of metrics) {
|
|
729
|
+
const { name, values } = metric
|
|
730
|
+
|
|
731
|
+
if (!metricsNames.includes(name)) continue
|
|
732
|
+
if (!values || values.length === 0) continue
|
|
733
|
+
|
|
734
|
+
const labels = values[0].labels
|
|
735
|
+
const serviceId = labels?.serviceId
|
|
736
|
+
|
|
737
|
+
if (!serviceId) {
|
|
738
|
+
throw new Error('Missing serviceId label in metrics')
|
|
744
739
|
}
|
|
740
|
+
|
|
741
|
+
let serviceMetrics = servicesMetrics[serviceId]
|
|
742
|
+
if (!serviceMetrics) {
|
|
743
|
+
serviceMetrics = {
|
|
744
|
+
cpu: 0,
|
|
745
|
+
rss: 0,
|
|
746
|
+
totalHeapSize: 0,
|
|
747
|
+
usedHeapSize: 0,
|
|
748
|
+
newSpaceSize: 0,
|
|
749
|
+
oldSpaceSize: 0,
|
|
750
|
+
elu: 0,
|
|
751
|
+
latency: {
|
|
752
|
+
p50: 0,
|
|
753
|
+
p90: 0,
|
|
754
|
+
p95: 0,
|
|
755
|
+
p99: 0
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
servicesMetrics[serviceId] = serviceMetrics
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
parsePromMetric(serviceMetrics, metric)
|
|
745
762
|
}
|
|
746
763
|
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
const elu = eventLoopUtilizationMetric.values[0].value
|
|
750
|
-
const totalHeapSize = totalHeapSizeMetric.values[0].value
|
|
751
|
-
const usedHeapSize = usedHeapSizeMetric.values[0].value
|
|
752
|
-
const newSpaceSize = newSpaceSizeTotalMetric.value
|
|
753
|
-
const oldSpaceSize = oldSpaceSizeTotalMetric.value
|
|
764
|
+
function parsePromMetric (serviceMetrics, promMetric) {
|
|
765
|
+
const { name } = promMetric
|
|
754
766
|
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
767
|
+
if (name === 'process_cpu_percent_usage') {
|
|
768
|
+
serviceMetrics.cpu = promMetric.values[0].value
|
|
769
|
+
return
|
|
770
|
+
}
|
|
771
|
+
if (name === 'process_resident_memory_bytes') {
|
|
772
|
+
serviceMetrics.rss = promMetric.values[0].value
|
|
773
|
+
return
|
|
774
|
+
}
|
|
775
|
+
if (name === 'nodejs_heap_size_total_bytes') {
|
|
776
|
+
serviceMetrics.totalHeapSize = promMetric.values[0].value
|
|
777
|
+
return
|
|
778
|
+
}
|
|
779
|
+
if (name === 'nodejs_heap_size_used_bytes') {
|
|
780
|
+
serviceMetrics.usedHeapSize = promMetric.values[0].value
|
|
781
|
+
return
|
|
782
|
+
}
|
|
783
|
+
if (name === 'nodejs_heap_space_size_total_bytes') {
|
|
784
|
+
const newSpaceSize = promMetric.values.find(value => value.labels.space === 'new')
|
|
785
|
+
const oldSpaceSize = promMetric.values.find(value => value.labels.space === 'old')
|
|
786
|
+
|
|
787
|
+
serviceMetrics.newSpaceSize = newSpaceSize.value
|
|
788
|
+
serviceMetrics.oldSpaceSize = oldSpaceSize.value
|
|
789
|
+
return
|
|
790
|
+
}
|
|
791
|
+
if (name === 'nodejs_eventloop_utilization') {
|
|
792
|
+
serviceMetrics.elu = promMetric.values[0].value
|
|
793
|
+
return
|
|
794
|
+
}
|
|
795
|
+
if (name === 'http_request_all_summary_seconds') {
|
|
796
|
+
serviceMetrics.latency = {
|
|
797
|
+
p50: promMetric.values.find(value => value.labels.quantile === 0.5)?.value || 0,
|
|
798
|
+
p90: promMetric.values.find(value => value.labels.quantile === 0.9)?.value || 0,
|
|
799
|
+
p95: promMetric.values.find(value => value.labels.quantile === 0.95)?.value || 0,
|
|
800
|
+
p99: promMetric.values.find(value => value.labels.quantile === 0.99)?.value || 0
|
|
771
801
|
}
|
|
772
802
|
}
|
|
773
803
|
}
|
|
774
804
|
|
|
775
|
-
return
|
|
805
|
+
return {
|
|
806
|
+
version: 1,
|
|
807
|
+
date: new Date().toISOString(),
|
|
808
|
+
services: servicesMetrics
|
|
809
|
+
}
|
|
776
810
|
} catch (err) {
|
|
777
811
|
// If any metric is missing, return nothing
|
|
778
812
|
this.logger.warn({ err }, 'Cannot fetch metrics')
|
package/lib/worker/http-cache.js
CHANGED
|
@@ -3,27 +3,53 @@
|
|
|
3
3
|
const { Readable, Writable } = require('node:stream')
|
|
4
4
|
const { kITC } = require('./symbols')
|
|
5
5
|
|
|
6
|
+
const noop = () => {}
|
|
7
|
+
|
|
6
8
|
class RemoteCacheStore {
|
|
9
|
+
#onRequest
|
|
10
|
+
#onCacheHit
|
|
11
|
+
#onCacheMiss
|
|
12
|
+
#logger
|
|
13
|
+
|
|
14
|
+
constructor (opts = {}) {
|
|
15
|
+
this.#onRequest = opts.onRequest ?? noop
|
|
16
|
+
this.#onCacheHit = opts.onCacheHit ?? noop
|
|
17
|
+
this.#onCacheMiss = opts.onCacheMiss ?? noop
|
|
18
|
+
this.#logger = opts.logger
|
|
19
|
+
}
|
|
20
|
+
|
|
7
21
|
async get (request) {
|
|
22
|
+
try {
|
|
23
|
+
this.#onRequest(request)
|
|
24
|
+
} catch (err) {
|
|
25
|
+
this.#logger.error(err, 'Error in onRequest http cache hook')
|
|
26
|
+
}
|
|
27
|
+
|
|
8
28
|
const itc = globalThis[kITC]
|
|
9
29
|
if (!itc) return
|
|
10
30
|
|
|
11
31
|
const cachedValue = await itc.send('getHttpCacheValue', {
|
|
12
32
|
request: this.#sanitizeRequest(request)
|
|
13
33
|
})
|
|
14
|
-
if (!cachedValue)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
})
|
|
34
|
+
if (!cachedValue) {
|
|
35
|
+
try {
|
|
36
|
+
this.#onCacheMiss(request)
|
|
37
|
+
} catch (err) {
|
|
38
|
+
this.#logger.error(err, 'Error in onCacheMiss http cache hook')
|
|
39
|
+
}
|
|
40
|
+
return
|
|
41
|
+
}
|
|
23
42
|
|
|
43
|
+
const readable = new Readable({ read () {} })
|
|
24
44
|
readable.push(cachedValue.payload)
|
|
25
45
|
readable.push(null)
|
|
26
46
|
|
|
47
|
+
try {
|
|
48
|
+
this.#onCacheHit(request, cachedValue.response)
|
|
49
|
+
} catch (err) {
|
|
50
|
+
this.#logger.error(err, 'Error in onCacheHit http cache hook')
|
|
51
|
+
}
|
|
52
|
+
|
|
27
53
|
return {
|
|
28
54
|
...cachedValue.response,
|
|
29
55
|
body: readable
|
package/lib/worker/main.js
CHANGED
|
@@ -159,7 +159,18 @@ async function main () {
|
|
|
159
159
|
setGlobalDispatcher(
|
|
160
160
|
getGlobalDispatcher().compose(
|
|
161
161
|
undici.interceptors.cache({
|
|
162
|
-
store: new RemoteCacheStore(
|
|
162
|
+
store: new RemoteCacheStore({
|
|
163
|
+
onRequest: (opts) => {
|
|
164
|
+
globalThis.platformatic?.onHttpCacheRequest?.(opts)
|
|
165
|
+
},
|
|
166
|
+
onCacheHit: (opts) => {
|
|
167
|
+
globalThis.platformatic?.onHttpCacheHit?.(opts)
|
|
168
|
+
},
|
|
169
|
+
onCacheMiss: (opts) => {
|
|
170
|
+
globalThis.platformatic?.onHttpCacheMiss?.(opts)
|
|
171
|
+
},
|
|
172
|
+
logger: globalThis.platformatic.logger
|
|
173
|
+
}),
|
|
163
174
|
methods: config.httpCache.methods ?? ['GET', 'HEAD']
|
|
164
175
|
})
|
|
165
176
|
)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/runtime",
|
|
3
|
-
"version": "2.26.
|
|
3
|
+
"version": "2.26.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -35,14 +35,15 @@
|
|
|
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": "2.26.
|
|
39
|
-
"@platformatic/db": "2.26.
|
|
40
|
-
"@platformatic/
|
|
41
|
-
"@platformatic/
|
|
42
|
-
"@platformatic/sql-graphql": "2.26.
|
|
43
|
-
"@platformatic/
|
|
38
|
+
"@platformatic/composer": "2.26.1",
|
|
39
|
+
"@platformatic/db": "2.26.1",
|
|
40
|
+
"@platformatic/service": "2.26.1",
|
|
41
|
+
"@platformatic/sql-mapper": "2.26.1",
|
|
42
|
+
"@platformatic/sql-graphql": "2.26.1",
|
|
43
|
+
"@platformatic/node": "2.26.1"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
+
"@fastify/accepts": "^5.0.0",
|
|
46
47
|
"@fastify/error": "^4.0.0",
|
|
47
48
|
"@fastify/websocket": "^11.0.0",
|
|
48
49
|
"@hapi/topo": "^6.0.2",
|
|
@@ -72,13 +73,13 @@
|
|
|
72
73
|
"undici": "^7.0.0",
|
|
73
74
|
"undici-thread-interceptor": "^0.10.0",
|
|
74
75
|
"ws": "^8.16.0",
|
|
75
|
-
"@platformatic/
|
|
76
|
-
"@platformatic/
|
|
77
|
-
"@platformatic/itc": "2.26.
|
|
78
|
-
"@platformatic/
|
|
79
|
-
"@platformatic/
|
|
80
|
-
"@platformatic/
|
|
81
|
-
"@platformatic/
|
|
76
|
+
"@platformatic/generators": "2.26.1",
|
|
77
|
+
"@platformatic/basic": "2.26.1",
|
|
78
|
+
"@platformatic/itc": "2.26.1",
|
|
79
|
+
"@platformatic/ts-compiler": "2.26.1",
|
|
80
|
+
"@platformatic/utils": "2.26.1",
|
|
81
|
+
"@platformatic/telemetry": "2.26.1",
|
|
82
|
+
"@platformatic/config": "2.26.1"
|
|
82
83
|
},
|
|
83
84
|
"scripts": {
|
|
84
85
|
"test": "npm run lint && borp --concurrency=1 --timeout=300000 && tsd",
|
package/schema.json
CHANGED